/**************************************************************************** * Copyright (c) 2017 Zheng, Lei (realthunder) * * * * 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" #ifndef _PreComp_ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Area.h" #include "../libarea/Area.h" using namespace Path; CAreaParams::CAreaParams() :PARAM_INIT(PARAM_FNAME,AREA_PARAMS_CAREA) {} AreaParams::AreaParams() :PARAM_INIT(PARAM_FNAME,AREA_PARAMS_AREA) {} CAreaConfig::CAreaConfig(const CAreaParams &p, bool noFitArcs) { #define AREA_CONF_SAVE_AND_APPLY(_param) \ PARAM_FNAME(_param) = BOOST_PP_CAT(CArea::get_,PARAM_FARG(_param))();\ BOOST_PP_CAT(CArea::set_,PARAM_FARG(_param))(p.PARAM_FNAME(_param)); PARAM_FOREACH(AREA_CONF_SAVE_AND_APPLY,AREA_PARAMS_CAREA) // Arc fitting is lossy. We shall reduce the number of unnecessary fit if(noFitArcs) CArea::set_fit_arcs(false); } CAreaConfig::~CAreaConfig() { #define AREA_CONF_RESTORE(_param) \ BOOST_PP_CAT(CArea::set_,PARAM_FARG(_param))(PARAM_FNAME(_param)); PARAM_FOREACH(AREA_CONF_RESTORE,AREA_PARAMS_CAREA) } ////////////////////////////////////////////////////////////////////////////// TYPESYSTEM_SOURCE(Path::Area, Base::BaseClass); bool Area::s_aborting; Area::Area(const AreaParams *params) :myParams(s_params) ,myHaveFace(false) ,myHaveSolid(false) ,myShapeDone(false) { if(params) setParams(*params); } Area::Area(const Area &other, bool deep_copy) :Base::BaseClass(other) ,myShapes(other.myShapes) ,myTrsf(other.myTrsf) ,myParams(other.myParams) ,myWorkPlane(other.myWorkPlane) ,myHaveFace(other.myHaveFace) ,myHaveSolid(other.myHaveSolid) ,myShapeDone(false) { if(!deep_copy || !other.isBuilt()) return; if(other.myArea) myArea.reset(new CArea(*other.myArea)); myShapePlane = other.myShapePlane; myShape = other.myShape; myShapeDone = other.myShapeDone; mySections.reserve(other.mySections.size()); for(shared_ptr area:mySections) mySections.push_back(make_shared(*area,true)); } Area::~Area() { clean(); } void Area::setPlane(const TopoDS_Shape &shape) { clean(); if(shape.IsNull()) { myWorkPlane.Nullify(); return; } gp_Trsf trsf; TopoDS_Shape plane = findPlane(shape,trsf); if (plane.IsNull()) throw Base::ValueError("shape is not planar"); myWorkPlane = plane; myTrsf = trsf; } bool Area::isCoplanar(const TopoDS_Shape &s1, const TopoDS_Shape &s2) { if(s1.IsNull() || s2.IsNull()) return false; if(s1.IsEqual(s2)) return true; TopoDS_Builder builder; TopoDS_Compound comp; builder.MakeCompound(comp); builder.Add(comp,s1); builder.Add(comp,s2); BRepLib_FindSurface planeFinder(comp,-1,Standard_True); return planeFinder.Found(); } int Area::addShape(CArea &area, const TopoDS_Shape &shape, const gp_Trsf *trsf, double deflection, const TopoDS_Shape *plane, bool force_coplanar, CArea *areaOpen, bool to_edges, bool reorient) { bool haveShape = false; int skipped = 0; for (TopExp_Explorer it(shape, TopAbs_FACE); it.More(); it.Next()) { haveShape = true; const TopoDS_Face &face = TopoDS::Face(it.Current()); if(plane && !isCoplanar(face,*plane)) { ++skipped; if(force_coplanar) continue; } for (TopExp_Explorer it(face, TopAbs_WIRE); it.More(); it.Next()) addWire(area,TopoDS::Wire(it.Current()),trsf,deflection); } if(haveShape) return skipped; CArea _area; CArea _areaOpen; for (TopExp_Explorer it(shape, TopAbs_WIRE); it.More(); it.Next()) { haveShape = true; const TopoDS_Wire &wire = TopoDS::Wire(it.Current()); if(plane && !isCoplanar(wire,*plane)) { ++skipped; if(force_coplanar) continue; } if(BRep_Tool::IsClosed(wire)) addWire(_area,wire,trsf,deflection); else if(to_edges) { for (TopExp_Explorer it(wire, TopAbs_EDGE); it.More(); it.Next()) addWire(_areaOpen,BRepBuilderAPI_MakeWire( TopoDS::Edge(it.Current())).Wire(),trsf,deflection,true); }else addWire(_areaOpen,wire,trsf,deflection); } if(!haveShape) { for (TopExp_Explorer it(shape, TopAbs_EDGE); it.More(); it.Next()) { if(plane && !isCoplanar(it.Current(),*plane)) { ++skipped; if(force_coplanar) continue; } TopoDS_Wire wire = BRepBuilderAPI_MakeWire( TopoDS::Edge(it.Current())).Wire(); addWire(BRep_Tool::IsClosed(wire)?_area:_areaOpen,wire,trsf,deflection); } } if(reorient) _area.Reorder(); area.m_curves.splice(area.m_curves.end(),_area.m_curves); if(areaOpen) areaOpen->m_curves.splice(areaOpen->m_curves.end(),_areaOpen.m_curves); else area.m_curves.splice(area.m_curves.end(),_areaOpen.m_curves); return skipped; } void Area::addWire(CArea &area, const TopoDS_Wire& wire, const gp_Trsf *trsf, double deflection, bool to_edges) { CCurve ccurve; BRepTools_WireExplorer xp(trsf?TopoDS::Wire( wire.Moved(TopLoc_Location(*trsf))):wire); gp_Pnt p = BRep_Tool::Pnt(xp.CurrentVertex()); ccurve.append(CVertex(Point(p.X(),p.Y()))); for (;xp.More();xp.Next()) { const TopoDS_Edge &edge = TopoDS::Edge(xp.Current()); BRepAdaptor_Curve curve(edge); bool reversed = (xp.Current().Orientation()==TopAbs_REVERSED); p = curve.Value(reversed?curve.FirstParameter():curve.LastParameter()); switch (curve.GetType()) { case GeomAbs_Line: { ccurve.append(CVertex(Point(p.X(),p.Y()))); if(to_edges) { area.append(ccurve); ccurve.m_vertices.pop_front(); } break; } case GeomAbs_Circle:{ if(!to_edges) { double first = curve.FirstParameter(); double last = curve.LastParameter(); gp_Circ circle = curve.Circle(); gp_Dir dir = circle.Axis().Direction(); gp_Pnt center = circle.Location(); int type = dir.Z()<0?-1:1; if(reversed) type = -type; if(fabs(first-last)>M_PI) { // Split arc(circle) larger than half circle. Because gcode // can't handle full circle? gp_Pnt mid = curve.Value((last-first)*0.5+first); ccurve.append(CVertex(type,Point(mid.X(),mid.Y()), Point(center.X(),center.Y()))); } ccurve.append(CVertex(type,Point(p.X(),p.Y()), Point(center.X(),center.Y()))); break; } //fall through } default: { // Discretize all other type of curves GCPnts_QuasiUniformDeflection discretizer(curve, deflection, curve.FirstParameter(), curve.LastParameter()); if (discretizer.IsDone () && discretizer.NbPoints () > 1) { int nbPoints = discretizer.NbPoints (); //strangly OCC discretizer points are one-based, not zero-based, why? if(reversed) { for (int i=nbPoints-1; i>=1; --i) { gp_Pnt pt = discretizer.Value (i); ccurve.append(CVertex(Point(pt.X(),pt.Y()))); if(to_edges) { area.append(ccurve); ccurve.m_vertices.pop_front(); } } }else{ for (int i=2; i<=nbPoints; i++) { gp_Pnt pt = discretizer.Value (i); ccurve.append(CVertex(Point(pt.X(),pt.Y()))); if(to_edges) { area.append(ccurve); ccurve.m_vertices.pop_front(); } } } }else Standard_Failure::Raise("Curve discretization failed"); }} } if(!to_edges) { if(BRep_Tool::IsClosed(wire) && !ccurve.IsClosed()) { AREA_WARN("ccurve not closed"); ccurve.append(ccurve.m_vertices.front()); } area.append(ccurve); } } void Area::clean(bool deleteShapes) { myShapeDone = false; mySections.clear(); myShape.Nullify(); myArea.reset(); myAreaOpen.reset(); myShapePlane.Nullify(); if(deleteShapes){ myShapes.clear(); myHaveFace = false; myHaveSolid = false; } } static inline ClipperLib::ClipType toClipperOp(short op) { switch(op){ case Area::OperationUnion: return ClipperLib::ctUnion; break; case Area::OperationDifference: return ClipperLib::ctDifference; break; case Area::OperationIntersection: return ClipperLib::ctIntersection; break; case Area::OperationXor: return ClipperLib::ctXor; break; default: throw Base::ValueError("invalid Operation"); } } void Area::add(const TopoDS_Shape &shape,short op) { if(shape.IsNull()) throw Base::ValueError("null shape"); if(op!=OperationCompound) toClipperOp(op); bool haveSolid = false; for(TopExp_Explorer it(shape, TopAbs_SOLID);it.More();) { haveSolid = true; break; } //TODO: shall we support Shells? if((!haveSolid && myHaveSolid) || (haveSolid && !myHaveSolid && !myShapes.empty())) throw Base::ValueError("mixing solid and planar shapes is not allowed"); myHaveSolid = haveSolid; clean(); if(op!=OperationCompound && myShapes.empty()) op = OperationUnion; myShapes.push_back(Shape(op,shape)); } void Area::setParams(const AreaParams ¶ms) { #define AREA_SRC2(_param) params.PARAM_FNAME(_param) // Validate all enum type of parameters PARAM_ENUM_CHECK(AREA_SRC2,PARAM_ENUM_EXCEPT,AREA_PARAMS_CONF); if(params!=myParams) { clean(); myParams = params; } } void Area::addToBuild(CArea &area, const TopoDS_Shape &shape) { if(myParams.Fill==FillAuto && !myHaveFace) { TopExp_Explorer it(shape, TopAbs_FACE); myHaveFace = it.More(); } TopoDS_Shape plane = getPlane(); CArea areaOpen; mySkippedShapes += addShape(area,shape,&myTrsf,myParams.Deflection, myParams.Coplanar==CoplanarNone?NULL:&plane, myHaveSolid||myParams.Coplanar==CoplanarForce,&areaOpen, myParams.OpenMode==OpenModeEdges,myParams.Reorient); if(areaOpen.m_curves.size()) { if(&area == myArea.get() || myParams.OpenMode == OpenModeNone) myAreaOpen->m_curves.splice(myAreaOpen->m_curves.end(),areaOpen.m_curves); else AREA_WARN("open wires discarded in clipping shapes"); } } namespace Part { extern PartExport std::list sort_Edges(double tol3d, std::list& edges); } void Area::explode(const TopoDS_Shape &shape) { const TopoDS_Shape &plane = getPlane(); bool haveShape = false; for(TopExp_Explorer it(shape, TopAbs_FACE); it.More(); it.Next()) { haveShape = true; if(myParams.Coplanar!=CoplanarNone && !isCoplanar(it.Current(),plane)){ ++mySkippedShapes; if(myParams.Coplanar == CoplanarForce) continue; } for(TopExp_Explorer itw(it.Current(), TopAbs_WIRE); itw.More(); itw.Next()) { for(BRepTools_WireExplorer xp(TopoDS::Wire(itw.Current()));xp.More();xp.Next()) addWire(*myArea,BRepBuilderAPI_MakeWire( TopoDS::Edge(xp.Current())).Wire(),&myTrsf,myParams.Deflection,true); } } if(haveShape) return; for(TopExp_Explorer it(shape, TopAbs_EDGE); it.More(); it.Next()) { if(myParams.Coplanar!=CoplanarNone && !isCoplanar(it.Current(),plane)){ ++mySkippedShapes; if(myParams.Coplanar == CoplanarForce) continue; } addWire(*myArea,BRepBuilderAPI_MakeWire( TopoDS::Edge(it.Current())).Wire(),&myTrsf,myParams.Deflection,true); } } #if 0 static void show(const TopoDS_Shape &shape, const char *name) { App::Document *pcDoc = App::GetApplication().getActiveDocument(); if (!pcDoc) pcDoc = App::GetApplication().newDocument(); Part::Feature *pcFeature = (Part::Feature *)pcDoc->addObject("Part::Feature", name); // copy the data //TopoShape* shape = new MeshObject(*pShape->getTopoShapeObjectPtr()); pcFeature->Shape.setValue(shape); //pcDoc->recompute(); } #endif template static int foreachSubshape(const TopoDS_Shape &shape, Func func, int type=TopAbs_FACE) { bool haveShape = false; switch(type) { case TopAbs_FACE: for(TopExp_Explorer it(shape,TopAbs_FACE); it.More(); it.Next()) { haveShape = true; func(it.Current(),TopAbs_FACE); } if(haveShape) return TopAbs_FACE; //fall through case TopAbs_WIRE: for(TopExp_Explorer it(shape,TopAbs_WIRE); it.More(); it.Next()) { haveShape = true; func(it.Current(),TopAbs_WIRE); } if(haveShape) return TopAbs_WIRE; //fall through default: for(TopExp_Explorer it(shape,TopAbs_EDGE); it.More(); it.Next()) { haveShape = true; func(it.Current(),TopAbs_EDGE); } } return haveShape?TopAbs_EDGE:-1; } struct FindPlane { TopoDS_Shape &myPlaneShape; gp_Trsf &myTrsf; double &myZ; FindPlane(TopoDS_Shape &s, gp_Trsf &t, double &z) :myPlaneShape(s),myTrsf(t),myZ(z) {} void operator()(const TopoDS_Shape &shape, int) { gp_Trsf trsf; BRepLib_FindSurface finder(shape,-1,Standard_True); if (!finder.Found()) return; gp_Ax3 pos = GeomAdaptor_Surface(finder.Surface()).Plane().Position(); // It seemed that FindSurface disregard shape's transformation, // so we have to transformed the found plane manually, or is it?? pos.Transform(shape.Location().Transformation()); // We only use right hand coordinate, hence gp_Ax2 instead of gp_Ax3 // This means that no matter what the work plane face oriented, we // will treat it as face upward in a right hand coordinate system. if(!pos.Direct()) pos = gp_Ax3(pos.Ax2()); gp_Dir dir(pos.Direction()); trsf.SetTransformation(pos); if(fabs(dir.X()) pt.Z()) return; myZ = pt.Z(); }else if(!myPlaneShape.IsNull()) return; myPlaneShape = shape; myTrsf = trsf; } }; TopoDS_Shape Area::findPlane(const TopoDS_Shape &shape, gp_Trsf &trsf) { TopoDS_Shape plane; double top_z; foreachSubshape(shape,FindPlane(plane,trsf,top_z)); return plane; } std::vector > Area::makeSections( PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SECTION_EXTRA), const std::vector &_heights, const TopoDS_Shape §ion_plane) { TopoDS_Shape plane; gp_Trsf trsf; if(!section_plane.IsNull()) plane = findPlane(section_plane,trsf); else plane = getPlane(&trsf); if(plane.IsNull()) throw Base::ValueError("failed to obtain section plane"); TIME_INIT2(t,t1); TopLoc_Location loc(trsf); Bnd_Box bounds; for(const Shape &s : myShapes) { const TopoDS_Shape &shape = s.shape.Moved(loc); BRepBndLib::Add(shape, bounds, Standard_False); } bounds.SetGap(0.0); Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax); AREA_TRACE("section bounds X("< heights; double tolerance = 0.0; if(_heights.empty()) { double z; double d = fabs(myParams.Stepdown); if(myParams.SectionCount>1 && d 0.0) z = zMax-myParams.SectionOffset; else z = zMin+myParams.SectionOffset; }else if(mode == SectionModeWorkplane){ // Because we've transformed the shapes using the work plane so // that the work plane is aligned with xy0 plane, the starting Z // value shall be 0 minus the given section offset. Note the // section offset is relative to the starting Z if(myParams.Stepdown > 0.0) z = -myParams.SectionOffset; else z = myParams.SectionOffset; } else { gp_Pnt pt(0,0,myParams.SectionOffset); z = pt.Transformed(loc).Z(); } if(z > zMax) z = zMax; else if(z < zMin) z = zMin; double dz; if(myParams.Stepdown>0.0) { dz = z - zMin; tolerance = myParams.SectionTolerance; }else{ dz = zMax - z; tolerance = -myParams.SectionTolerance; } int count = myParams.SectionCount; if(count<0 || count*d > dz) count = floor(dz/d)+1; heights.reserve(count); for(int i=0;i0.0) break; }else if(zMax-z > sections; sections.reserve(heights.size()); tolerance *= 2.0; bool can_retry = fabs(tolerance)>Precision::Confusion(); TopLoc_Location locInverse(loc.Inverted()); for(double z : heights) { bool retried = !can_retry; while(true) { gp_Pln pln(gp_Pnt(0,0,z),gp_Dir(0,0,1)); Standard_Real a,b,c,d; pln.Coefficients(a,b,c,d); BRepLib_MakeFace mkFace(pln,xMin,xMax,yMin,yMax); const TopoDS_Shape &face = mkFace.Face(); shared_ptr area(new Area(&myParams)); area->setPlane(face.Moved(locInverse)); for(auto it=myShapes.begin();it!=myShapes.end();++it) { const auto &s = *it; BRep_Builder builder; TopoDS_Compound comp; builder.MakeCompound(comp); for(TopExp_Explorer xp(s.shape.Moved(loc), TopAbs_SOLID); xp.More(); xp.Next()) { Part::CrossSection section(a,b,c,xp.Current()); std::list wires = section.slice(-d); if(wires.empty()) { AREA_LOG("Section returns no wires"); continue; } Part::FaceMakerBullseye mkFace; mkFace.setPlane(pln); for(const TopoDS_Wire &wire : wires) mkFace.addWire(wire); try { mkFace.Build(); if (mkFace.Shape().IsNull()) AREA_WARN("FaceMakerBullseye return null shape on section"); else { builder.Add(comp,mkFace.Shape()); continue; } }catch (Base::Exception &e){ AREA_WARN("FaceMakerBullseye failed on section: " << e.what()); } for(const TopoDS_Wire &wire : wires) builder.Add(comp,wire); } // Make sure the compound has at least one edge TopExp_Explorer xp(comp,TopAbs_EDGE); if(xp.More()) { area->add(comp.Moved(locInverse),s.op); }else if(area->myShapes.empty()){ auto itNext = it; if(++itNext != myShapes.end() && (itNext->op==OperationIntersection || itNext->op==OperationDifference)) { break; } } } if(area->myShapes.size()){ sections.push_back(area); break; } if(retried) { AREA_WARN("Discard empty section"); break; }else{ AREA_TRACE("retry section " <"<m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves); pending = false; if(areaClip.m_curves.size()) { if(op == OperationCompound) myArea->m_curves.splice(myArea->m_curves.end(),areaClip.m_curves); else{ myArea->Clip(toClipperOp(op),&areaClip,SubjectFill,ClipFill); areaClip.m_curves.clear(); } } op=s.op; } addToBuild(op==OperationUnion?*myArea:areaClip,s.shape); pending = true; } if(mySkippedShapes && !myHaveSolid) AREA_WARN((myParams.Coplanar==CoplanarForce?"Skipped ":"Found ")<< mySkippedShapes<<" non coplanar shapes"); if(pending){ if(myParams.OpenMode!=OpenModeNone) myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves); if(op == OperationCompound) myArea->m_curves.splice(myArea->m_curves.end(),areaClip.m_curves); else{ myArea->Clip(toClipperOp(op),&areaClip,SubjectFill,ClipFill); } } myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves); //Reassemble wires after explode if(myParams.Explode) { std::list edges; gp_Trsf trsf(myTrsf.Inverted()); for(const auto &c : myArea->m_curves) { TopoDS_Wire wire = toShape(c,&trsf); if(wire.IsNull()) continue; TopExp_Explorer it(wire, TopAbs_EDGE); edges.push_back(TopoDS::Edge(it.Current())); } Area area(&myParams); area.myParams.Explode = false; area.myParams.Coplanar = CoplanarNone; area.myWorkPlane = getPlane(&area.myTrsf); while(edges.size()) { BRepBuilderAPI_MakeWire mkWire; for(const auto &e : Part::sort_Edges(myParams.Tolerance,edges)) mkWire.Add(TopoDS::Edge(e)); area.add(mkWire.Wire(),OperationCompound); } area.build(); myArea = std::move(area.myArea); } TIME_TRACE(t,"prepare"); }catch(...) { clean(); throw; } } TopoDS_Shape Area::toShape(CArea &area, short fill) { gp_Trsf trsf(myTrsf.Inverted()); bool bFill; switch(fill){ case Area::FillAuto: bFill = myHaveFace; break; case Area::FillFace: bFill = true; break; default: bFill = false; } if(myParams.FitArcs) { if(&area == myArea.get()) { CArea copy(area); copy.FitArcs(); return toShape(copy,bFill,&trsf); } area.FitArcs(); } return toShape(area,bFill,&trsf); } #define AREA_SECTION(_op,_index,...) do {\ if(mySections.size()) {\ if(_index>=(int)mySections.size())\ return TopoDS_Shape();\ if(_index<0) {\ BRep_Builder builder;\ TopoDS_Compound compound;\ builder.MakeCompound(compound);\ for(shared_ptr area : mySections){\ const TopoDS_Shape &s = area->_op(-1, ## __VA_ARGS__);\ if(s.IsNull()) continue;\ builder.Add(compound,s);\ }\ for(TopExp_Explorer it(compound,TopAbs_EDGE);it.More();)\ return compound;\ return TopoDS_Shape();\ }\ return mySections[_index]->_op(-1, ## __VA_ARGS__);\ }\ }while(0) TopoDS_Shape Area::getShape(int index) { build(); AREA_SECTION(getShape,index); if(myShapeDone) return myShape; if(!myArea) return TopoDS_Shape(); CAreaConfig conf(myParams); #define AREA_MY(_param) myParams.PARAM_FNAME(_param) // if no offset or thicken, try pocket if(fabs(myParams.Offset) < Precision::Confusion() && !myParams.Thicken) { if(myParams.PocketMode == PocketModeNone) { myShape = toShape(*myArea,myParams.Fill); myShapeDone = true; return myShape; } myShape = makePocket(-1,PARAM_FIELDS(AREA_MY,AREA_PARAMS_POCKET)); myShapeDone = true; return myShape; } // if no pocket, do offset or thicken if(myParams.PocketMode == PocketModeNone){ myShape = makeOffset(-1,PARAM_FIELDS(AREA_MY,AREA_PARAMS_OFFSET)); myShapeDone = true; return myShape; } TIME_INIT(t); // do offset first, then pocket the inner most offseted shape std::list > areas; makeOffset(areas,PARAM_FIELDS(AREA_MY,AREA_PARAMS_OFFSET)); if(areas.empty()) areas.push_back(make_shared(*myArea)); Area areaPocket(&myParams); bool front = true; if(areas.size()>1) { double step = myParams.Stepover; if(fabs(step)0; } // for pocketing, we discard the outer most offset wire in order to achieve // the effect of offseting shape first than pocket, where the actual offset // path is not wanted. For extra outline profiling, add extra_offset if(front) { areaPocket.add(toShape(*areas.front(),myParams.Fill)); areas.pop_back(); }else{ areaPocket.add(toShape(*areas.back(),myParams.Fill)); areas.pop_front(); } BRep_Builder builder; TopoDS_Compound compound; builder.MakeCompound(compound); short fill = myParams.Thicken?FillFace:FillNone; TIME_INIT(t2); DURATION_INIT(d); for(shared_ptr area : areas) { if(myParams.Thicken){ area->Thicken(myParams.ToolRadius); DURATION_PLUS(d,t2); } const TopoDS_Shape &shape = toShape(*area,fill); if(shape.IsNull()) continue; builder.Add(compound,shape); } if(myParams.Thicken) DURATION_PRINT(d,"Thicken"); // make sure the compound has at least one edge for(TopExp_Explorer it(compound,TopAbs_EDGE);it.More();) { builder.Add(compound,areaPocket.makePocket( -1,PARAM_FIELDS(AREA_MY,AREA_PARAMS_POCKET))); myShape = compound; break; } myShapeDone = true; TIME_PRINT(t,"total"); return myShape; } TopoDS_Shape Area::makeOffset(int index,PARAM_ARGS(PARAM_FARG,AREA_PARAMS_OFFSET)) { build(); AREA_SECTION(makeOffset,index,PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_OFFSET)); std::list > areas; makeOffset(areas,PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_OFFSET)); if(areas.empty()) { if(myParams.Thicken && myParams.ToolRadius>Precision::Confusion()) { CArea area(*myArea); TIME_INIT(t); area.Thicken(myParams.ToolRadius); TIME_PRINT(t,"Thicken"); return toShape(area,FillFace); } return TopoDS_Shape(); } BRep_Builder builder; TopoDS_Compound compound; builder.MakeCompound(compound); TIME_INIT(t); DURATION_INIT(d); for(shared_ptr area : areas) { short fill; if(myParams.Thicken && myParams.ToolRadius>Precision::Confusion()) { area->Thicken(myParams.ToolRadius); DURATION_PLUS(d,t); fill = FillFace; }else if(areas.size()==1) fill = myParams.Fill; else fill = FillNone; const TopoDS_Shape &shape = toShape(*area,fill); if(shape.IsNull()) continue; builder.Add(compound,shape); } if(myParams.Thicken && myParams.ToolRadius>Precision::Confusion()) DURATION_PRINT(d,"Thicken"); for(TopExp_Explorer it(compound,TopAbs_EDGE);it.More();) return compound; return TopoDS_Shape(); } void Area::makeOffset(list > &areas, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_OFFSET)) { if(fabs(offset) 0) { count += extra_pass; }else{ if(stepover>0 || offset>0) throw Base::ValueError("invalid extra count"); // In this case, we loop until no outputs from clipper count=-1; } } PARAM_ENUM_CONVERT(AREA_SRC,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_OFFSET_CONF); #ifdef AREA_OFFSET_ALGO PARAM_ENUM_CONVERT(AREA_SRC,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_CLIPPER_FILL); #endif for(int i=0;count<0||i()); CArea &area = *areas.back(); CArea areaOpen; #ifdef AREA_OFFSET_ALGO if(myParams.Algo == Area::Algolibarea) { for(const CCurve &c : myArea->m_curves) { if(c.IsClosed()) area.append(c); else areaOpen.append(c); } }else #endif area = *myArea; #ifdef AREA_OFFSET_ALGO switch(myParams.Algo){ case Area::Algolibarea: // libarea somehow fails offset without Reorder, but ClipperOffset // works okay. Don't know why area.Reorder(); area.Offset(-offset); if(areaOpen.m_curves.size()) { areaOpen.Thicken(offset); area.Clip(ClipperLib::ctUnion,&areaOpen,SubjectFill,ClipFill); } break; case Area::AlgoClipperOffset: #endif area.OffsetWithClipper(offset,JoinType,EndType, myParams.MiterLimit,myParams.RoundPreceision); #ifdef AREA_OFFSET_ALGO break; } #endif if(count>1) TIME_PRINT(t1,"makeOffset " << i << '/' << count); if(area.m_curves.empty()) return; } TIME_PRINT(t,"makeOffset count: " << count); } TopoDS_Shape Area::makePocket(int index, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_POCKET)) { if(tool_radius < Precision::Confusion()) throw Base::ValueError("tool radius too small"); if(stepover == 0.0) stepover = tool_radius; if(stepover < Precision::Confusion()) throw Base::ValueError("stepover too small"); if(mode == Area::PocketModeNone) return TopoDS_Shape(); build(); AREA_SECTION(makePocket,index,PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_POCKET)); TIME_INIT(t); PocketMode pm; switch(mode) { case Area::PocketModeZigZag: pm = ZigZagPocketMode; break; case Area::PocketModeSpiral: pm = SpiralPocketMode; break; case Area::PocketModeOffset: { PARAM_DECLARE_INIT(PARAM_FNAME,AREA_PARAMS_OFFSET); Offset = -tool_radius-extra_offset; ExtraPass = -1; Stepover = -stepover; return makeOffset(index,PARAM_FIELDS(PARAM_FNAME,AREA_PARAMS_OFFSET)); }case Area::PocketModeZigZagOffset: pm = ZigZagThenSingleOffsetPocketMode; break; default: throw Base::ValueError("unknown poket mode"); } CAreaConfig conf(myParams); CAreaPocketParams params( tool_radius,extra_offset,stepover,from_center,pm,zig_angle); CArea in(*myArea),out; // MakePcoketToolPath internally uses libarea Offset which somehow demands // reorder before input, otherwise nothing is shown. in.Reorder(); in.MakePocketToolpath(out.m_curves,params); TIME_PRINT(t,"makePocket"); if(myParams.Thicken){ out.Thicken(tool_radius); return toShape(out,FillFace); }else return toShape(out,FillNone); } static inline bool IsLeft(const gp_Pnt &a, const gp_Pnt &b, const gp_Pnt &c) { return ((b.X() - a.X())*(c.Y() - a.Y()) - (b.Y() - a.Y())*(c.X() - a.X())) > 0; } TopoDS_Wire Area::toShape(const CCurve &c, const gp_Trsf *trsf) { BRepBuilderAPI_MakeWire mkWire; gp_Pnt pstart,pt; bool first = true; for(const CVertex &v : c.m_vertices){ if(first){ first = false; pstart = pt = gp_Pnt(v.m_p.x,v.m_p.y,0); continue; } gp_Pnt pnext(v.m_p.x,v.m_p.y,0); if(pnext.Distance(pt) Precision::Confusion()) { double d = pt.Distance(pnext); double q = sqrt(r*r - d*d*0.25); double x = (pt.X()+pnext.X())*0.5; double y = (pt.Y()+pnext.Y())*0.5; double dx = q*(pt.Y()-pnext.Y())/d; double dy = q*(pnext.X()-pt.X())/d; gp_Pnt newCenter(x + dx, y + dy,0); if(IsLeft(pt,pnext,center) != IsLeft(pt,pnext,newCenter)) { newCenter.SetX(x - dx); newCenter.SetY(y - dy); } AREA_WARN("Arc correction: "<"< points; bool isClosed; inline const gp_Pnt &pstart() const{ return points.front(); } inline const gp_Pnt &pend() const{ return isClosed?pstart():points.back(); } }; typedef std::list Wires; typedef std::pair RValue; struct RGetter { typedef const gp_Pnt& result_type; result_type operator()(const RValue &v) const { return v.first->points[v.second]; } }; typedef bgi::linear<16> RParameters; typedef bgi::rtree RTree; struct RTreeParams { double abscissa; int k; #ifdef AREA_TIME_ENABLE TIME_DURATION qd; //rtree query duration TIME_DURATION bd; //rtree build duration TIME_DURATION rd; //rtree remove duration TIME_DURATION xd; //BRepExtrema_DistShapeShape duration #endif RTreeParams(double _a, int _k) :abscissa(_a),k(_k) #ifdef AREA_TIME_ENABLE ,qd(0),bd(0),rd(0),xd(0) #endif {} }; bool operator<(const Wires::iterator &a, const Wires::iterator &b) { return &(*a) < &(*b); } typedef std::map RResults; struct GetWires { Wires &wires; RTree &rtree; RTreeParams ¶ms; GetWires(std::list &ws, RTree &rt, RTreeParams &rp) :wires(ws),rtree(rt),params(rp) {} void operator()(const TopoDS_Shape &shape, int type) { wires.push_back(WireInfo()); WireInfo &info = wires.back(); if(type == TopAbs_WIRE) info.wire = TopoDS::Wire(shape); else info.wire = BRepBuilderAPI_MakeWire(TopoDS::Edge(shape)).Wire(); info.isClosed = BRep_Tool::IsClosed(info.wire); TIME_INIT(t); BRepTools_WireExplorer xp(info.wire); if(params.abscissawire; TopoDS_Shape support; bool support_edge; double d = 0; gp_Pnt p; bool done = false; bool is_start = false; if(BRep_Tool::IsClosed(wire)) { TIME_INIT(t); BRepExtrema_DistShapeShape extss(v,wire); if(extss.IsDone() && extss.NbSolution()) { d = extss.Value(); p = extss.PointOnShape2(1); support = extss.SupportOnShape2(1); support_edge = extss.SupportTypeShape2(1)==BRepExtrema_IsOnEdge; if(support_edge) extss.ParOnEdgeS2(1,myBestParameter); done = true; }else AREA_WARN("BRepExtrema_DistShapeShape failed"); DURATION_PLUS(myParams.xd,t); } if(!done){ double d1 = pt.Distance(it->pstart()); double d2 = pt.Distance(it->pend()); if(d1pstart(); is_start = true; }else{ d = d2; p = it->pend(); is_start = false; } } if(!first && d>=best_d) continue; first = false; myBestPt = p; myBestWire = it; best_d = d; myRebase = done; myStart = is_start; if(done) { mySupport = support; mySupportEdge = support_edge; } } return best_d; } //Assumes nearest() has been called. Rebased the best wire //to begin with the best point. Currently only works with closed wire TopoDS_Shape rebaseWire(gp_Pnt &pend, double min_dist) { BRepBuilderAPI_MakeWire mkWire; TopoDS_Shape estart; TopoDS_Edge eend; for(int state=0;state<3;++state) { BRepTools_WireExplorer xp(TopoDS::Wire(myBestWire->wire)); gp_Pnt pprev(BRep_Tool::Pnt(xp.CurrentVertex())); //checking the case of bestpoint == wire start if(state==0 && !mySupportEdge && pprev.Distance(myBestPt)pend(); return myBestWire->wire; } gp_Pnt pt; for(;xp.More();xp.Next(),pprev=pt) { const auto &edge = xp.Current(); //state==2 means we are in second pass. estart marks the new //start of the wire. so seeing estart means we're done if(state==2 && estart.IsEqual(edge)) break; // Edge split not working if using BRepAdaptor_Curve. // BRepBuilderAPI_MakeEdge always fails with // PointProjectionFailed. Why?? Standard_Real first,last; const Handle_Geom_Curve & curve = BRep_Tool::Curve(edge, first, last); pt = curve->Value(last); bool reversed; if(pprev.Distance(pt)Value(first); }else reversed = false; //state!=0 means we've found the new start of wire, now just //keep adding new edges if(state) { mkWire.Add(edge); continue; } //state==0 means we are looking for the new start if(mySupportEdge) { //if best point is on some edge, split the edge in half if(edge.IsEqual(mySupport)) { double d1 = pprev.Distance(myBestPt); double d2 = pt.Distance(myBestPt); if(d1>min_dist && d2>min_dist) { BRepBuilderAPI_MakeEdge mkEdge1,mkEdge2; if(reversed) { mkEdge1.Init(curve, myBestPt, pprev); mkEdge2.Init(curve, pt, myBestPt); }else{ mkEdge1.Init(curve, pprev, myBestPt); mkEdge2.Init(curve, myBestPt, pt); } // Using parameter is not working, why? // if(reversed) { // mkEdge1.Init(curve, myBestParameter, last); // mkEdge2.Init(curve, first, myBestParameter); // }else{ // mkEdge1.Init(curve, first, myBestParameter); // mkEdge2.Init(curve, myBestParameter, last); // } if(mkEdge1.IsDone() && mkEdge2.IsDone()) { if(reversed) { eend = TopoDS::Edge(mkEdge1.Edge().Reversed()); mkWire.Add(TopoDS::Edge(mkEdge2.Edge().Reversed())); }else{ eend = mkEdge1.Edge(); mkWire.Add(mkEdge2.Edge()); } pend = myBestPt; estart = mySupport; state = 1; AREA_TRACE((reversed?"reversed ":"")<<"edge split "<start"); estart = edge; state = 1; mkWire.Add(edge); }else{ // AREA_TRACE("split edge->end"); mySupportEdge = false; myBestPt = pt; continue; } } }else if(myBestPt.Distance(pprev)pend(); return myBestWire->wire; } } if(!eend.IsNull()) mkWire.Add(eend); if(mkWire.IsDone()) return mkWire.Wire(); AREA_WARN("wire rebase failed"); pend = myBestWire->pend(); return myBestWire->wire; } std::list sortWires(const gp_Pnt &pstart, gp_Pnt &pend,double min_dist) { if(pstart.Distance(myStartPt)>Precision::Confusion()) nearest(pstart); std::list wires; if(min_dist < 0.01) min_dist = 0.01; while(true) { if(myRebase) { pend = myBestPt; wires.push_back(rebaseWire(pend,min_dist)); }else if(!myStart){ wires.push_back(myBestWire->wire.Reversed()); pend = myBestWire->pstart(); }else{ wires.push_back(myBestWire->wire); pend = myBestWire->pend(); } TIME_INIT(t); for(size_t i=0,count=myBestWire->points.size();i &myList; gp_Trsf &myTrsf; short *myArcPlane; bool &myArcPlaneFound; RTreeParams &myParams; ShapeInfoBuilder(bool &plane_found, short *arc_plane, gp_Trsf &trsf, std::list &list, RTreeParams ¶ms) :myList(list) ,myTrsf(trsf) ,myArcPlane(arc_plane) ,myArcPlaneFound(plane_found), myParams(params) {} void operator()(const TopoDS_Shape &shape, int type) { BRepLib_FindSurface finder(shape,-1,Standard_True); if(!finder.Found()) { myList.push_back(ShapeInfo(shape,myParams)); return; } myList.push_back(ShapeInfo(finder,shape,myParams)); if(myArcPlane==NULL || myArcPlaneFound || *myArcPlane==Area::ArcPlaneNone || *myArcPlane==Area::ArcPlaneVariable) return; if(type == TopAbs_EDGE) { BRepAdaptor_Curve curve(TopoDS::Edge(shape)); if(curve.GetType()!=GeomAbs_Circle) return; }else{ bool found = false; for(TopExp_Explorer it(shape,TopAbs_EDGE);it.More();it.Next()) { BRepAdaptor_Curve curve(TopoDS::Edge(it.Current())); if(curve.GetType()==GeomAbs_Circle) { found = true; break; } } if(!found) return; } gp_Ax3 pos = myList.back().myPln.Position(); if(!pos.Direct()) pos = gp_Ax3(pos.Ax2()); const gp_Dir &dir = pos.Direction(); gp_Ax3 dstPos; bool x0 = fabs(dir.X()) Area::sortWires(const std::list &shapes, const gp_Pnt *_pstart, gp_Pnt *_pend, short *arc_plane, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SORT)) { std::list wires; if(shapes.empty()) return wires; if(sort_mode == SortModeNone) { for(auto &shape : shapes) { if (shape.IsNull()) continue; bool haveShape=false; for(TopExp_Explorer it(shape,TopAbs_WIRE);it.More();it.Next()) { haveShape=true; wires.push_back(it.Current()); } if(haveShape) continue; for(TopExp_Explorer it(shape,TopAbs_EDGE);it.More();it.Next()) wires.push_back(BRepBuilderAPI_MakeWire(TopoDS::Edge(it.Current())).Wire()); } return std::move(wires); } RTreeParams rparams(abscissa,nearest_k>0?nearest_k:1); std::list shape_list; TIME_INIT2(t,t1); gp_Trsf trsf; bool arcPlaneFound = false; if(sort_mode == SortMode3D) { TopoDS_Builder builder; TopoDS_Compound comp; builder.MakeCompound(comp); for(auto &shape : shapes) { if(!shape.IsNull()) builder.Add(comp,shape); } TopExp_Explorer xp(comp,TopAbs_EDGE); if(xp.More()) shape_list.push_back(ShapeInfo(comp,rparams)); }else{ //first pass, find plane of each shape for(auto &shape : shapes) { //explode the shape if(!shape.IsNull()){ foreachSubshape(shape,ShapeInfoBuilder( arcPlaneFound,arc_plane,trsf,shape_list,rparams)); } } TIME_PRINT(t1,"plane finding"); } if(shape_list.empty()) return wires; Bnd_Box bounds; gp_Pnt pstart,pend; if(_pstart) pstart = *_pstart; bool use_bound = fabs(pstart.X())myPlanar) continue; TopoDS_Builder builder; TopoDS_Compound comp; builder.MakeCompound(comp); bool empty = true; for(auto itNext3=itNext,itNext2=itNext;itNext2!=shape_list.end();itNext2=itNext3) { ++itNext3; if(!itNext2->myPlanar || !it->myPln.Position().IsCoplanar(itNext2->myPln.Position(), Precision::Confusion(),Precision::Confusion())) continue; if(itNext == itNext2) ++itNext; builder.Add(comp,itNext2->myShape); shape_list.erase(itNext2); empty = false; } if(!empty) { builder.Add(comp,it->myShape); it->myShape = comp; } } TIME_PRINT(t,"plane merging"); } DURATION_INIT(td); if(use_bound) { bounds.SetGap(0.0); Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax); AREA_TRACE("bound (" << xMin<<", "<myPlanar) d = it->myPln.Distance(pstart); else d = it->nearest(pstart); if(first || dsortWires(pstart,pend,min_dist)); pstart = pend; shape_list.erase(best_it); } if(_pend) *_pend = pend; DURATION_PRINT(rparams.bd,"rtree build"); DURATION_PRINT(rparams.qd,"rtree query"); DURATION_PRINT(rparams.rd,"rtree clean"); DURATION_PRINT(rparams.xd,"BRepExtrema"); TIME_PRINT(t,"sortWires total"); return std::move(wires); } static inline void addParameter(Command &cmd, const char *name, double last, double next, bool relative=false) { double d = next-last; if(fabs(d)Precision::Confusion() && clearance+(next.*getter)() < retraction) { last = pt; pt = next; (pt.*setter)((next.*getter)()+clearance); addCommand(path,last,pt,"G0"); addCommand(path,pt,next); }else addCommand(path,pt,next,"G0"); } static void addCommand(Toolpath &path, const gp_Pnt &pstart, const gp_Pnt &pend, const gp_Pnt ¢er, bool clockwise) { Command cmd; cmd.Name = clockwise?"G2":"G3"; addParameter(cmd,"I",pstart.X(),center.X(),true); addParameter(cmd,"J",pstart.Y(),center.Y(),true); addParameter(cmd,"K",pstart.Z(),center.Z(),true); addParameter(cmd,"X",pstart.X(),pend.X()); addParameter(cmd,"Y",pstart.Y(),pend.Y()); addParameter(cmd,"Z",pstart.Z(),pend.Z()); path.addCommand(cmd); } static inline void addCommand(Toolpath &path, const char *name) { Command cmd; cmd.Name = name; path.addCommand(cmd); } void Area::toPath(Toolpath &path, const std::list &shapes, const gp_Pnt *pstart, gp_Pnt *pend, PARAM_ARGS(PARAM_FARG,AREA_PARAMS_PATH)) { std::list wires; wires = sortWires(shapes,pstart,pend, PARAM_REF(PARAM_FARG,AREA_PARAMS_ARC_PLANE), PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_SORT)); // absolute mode addCommand(path,"G90"); short currentArcPlane = arc_plane; if(arc_plane==ArcPlaneZX) addCommand(path,"G18"); else if(arc_plane==ArcPlaneYZ) addCommand(path,"G19"); else currentArcPlane=ArcPlaneXY; threshold = fabs(threshold); if(threshold < Precision::Confusion()) threshold = Precision::Confusion(); clearance = fabs(clearance); AxisGetter getter = NULL; AxisSetter setter = NULL; retraction = fabs(retraction); if(retraction>Precision::Confusion()) { switch(retract_axis) { case RetractAxisX: getter = &gp_Pnt::X; setter = &gp_Pnt::SetX; break; case RetractAxisY: getter = &gp_Pnt::Y; setter = &gp_Pnt::SetY; break; case RetractAxisZ: getter = &gp_Pnt::Z; setter = &gp_Pnt::SetZ; break; } } gp_Pnt plast,p; if(pstart) plast = *pstart; bool first = true; bool arcWarned = false; for(const TopoDS_Shape &wire : wires) { BRepTools_WireExplorer xp(TopoDS::Wire(wire)); p = BRep_Tool::Pnt(xp.CurrentVertex()); if(first||p.Distance(plast)>threshold) addCommand(path,plast,p,getter,setter,retraction,clearance); else addCommand(path,plast,p); plast = p; first = false; for(;xp.More();xp.Next(),plast=p) { const auto &edge = xp.Current(); BRepAdaptor_Curve curve(edge); bool reversed = (edge.Orientation()==TopAbs_REVERSED); p = curve.Value(reversed?curve.FirstParameter():curve.LastParameter()); switch (curve.GetType()) { case GeomAbs_Line: { if(segmentation > Precision::Confusion()) { GCPnts_UniformAbscissa discretizer(curve, segmentation, curve.FirstParameter(), curve.LastParameter()); if (discretizer.IsDone () && discretizer.NbPoints () > 2) { int nbPoints = discretizer.NbPoints (); if(reversed) { for (int i=nbPoints-1; i>=1; --i) { gp_Pnt pt = curve.Value(discretizer.Parameter(i)); addCommand(path,plast,pt); plast = pt; } }else{ for (int i=2; i<=nbPoints; i++) { gp_Pnt pt = curve.Value(discretizer.Parameter(i)); addCommand(path,plast,pt); plast = pt; } } break; } } addCommand(path,plast,p); break; } case GeomAbs_Circle:{ const gp_Circ &circle = curve.Circle(); const gp_Dir &dir = circle.Axis().Direction(); short arcPlane = ArcPlaneNone; bool clockwise; const char *cmd; if(fabs(dir.X()) Precision::Confusion()) { GCPnts_UniformAbscissa discretizer(curve,segmentation,first,last); if (discretizer.IsDone () && discretizer.NbPoints () > 2) { int nbPoints = discretizer.NbPoints (); if(reversed) { for (int i=nbPoints-1; i>=1; --i) { gp_Pnt pt = curve.Value(discretizer.Parameter(i)); addCommand(path,plast,pt,center,clockwise); plast = pt; } }else{ for (int i=2; i<=nbPoints; i++) { gp_Pnt pt = curve.Value(discretizer.Parameter(i)); addCommand(path,plast,pt,center,clockwise); plast = pt; } } break; } } if(fabs(first-last)>M_PI) { // Split arc(circle) larger than half circle. gp_Pnt mid = curve.Value((last-first)*0.5+first); addCommand(path,plast,mid,center,clockwise); plast = mid; } addCommand(path,plast,p,center,clockwise); break; } if(!arcWarned){ arcWarned = true; AREA_WARN("arc plane not aligned, force discretization"); } AREA_TRACE("arc discretize " << AREA_XYZ(dir)); //fall through } default: { // Discretize all other type of curves GCPnts_QuasiUniformDeflection discretizer(curve, deflection, curve.FirstParameter(), curve.LastParameter()); if (discretizer.IsDone () && discretizer.NbPoints () > 1) { int nbPoints = discretizer.NbPoints (); if(reversed) { for (int i=nbPoints-1; i>=1; --i) { gp_Pnt pt = discretizer.Value (i); addCommand(path,plast,pt); plast = pt; } }else{ for (int i=2; i<=nbPoints; i++) { gp_Pnt pt = discretizer.Value (i); addCommand(path,plast,pt); plast = pt; } } }else Standard_Failure::Raise("Curve discretization failed"); }} } } } void Area::abort(bool aborting) { s_aborting = aborting; } bool Area::aborting() { return s_aborting; } AreaStaticParams::AreaStaticParams() :PARAM_INIT(PARAM_FNAME,AREA_PARAMS_EXTRA_CONF) {} AreaStaticParams Area::s_params; void Area::setDefaultParams(const AreaStaticParams ¶ms){ s_params = params; } const AreaStaticParams &Area::getDefaultParams() { return s_params; } #define AREA_LOG_CHECK_DEFINE(_1,_2,_elem) \ bool Area::BOOST_PP_CAT(_elem,Enabled)() {\ return s_params.LogLevel >= BOOST_PP_CAT(LogLevel,_elem);\ } BOOST_PP_SEQ_FOR_EACH(AREA_LOG_CHECK_DEFINE,_,AREA_PARAM_LOG_LEVEL)