diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index 36a31371f..01cd1e7e9 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -51,6 +51,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -64,11 +68,11 @@ using namespace Path; CAreaParams::CAreaParams() - :PARAM_INIT(NAME,AREA_PARAMS_CAREA) + :PARAM_INIT(PARAM_FNAME,AREA_PARAMS_CAREA) {} AreaParams::AreaParams() - :PARAM_INIT(NAME,AREA_PARAMS_BASE) + :PARAM_INIT(PARAM_FNAME,AREA_PARAMS_AREA) {} CAreaConfig::CAreaConfig(const CAreaParams &p, bool noFitArcs) @@ -98,9 +102,9 @@ CAreaConfig::~CAreaConfig() { TYPESYSTEM_SOURCE(Path::Area, Base::BaseClass); Area::Area(const AreaParams *params) -:myArea(NULL) -,myAreaOpen(NULL) -,myHaveFace(false) +:myHaveFace(false) +,myHaveSolid(false) +,myShapeDone(false) { if(params) setParams(*params); @@ -256,35 +260,51 @@ void Area::add(CArea &area, const TopoDS_Wire& wire, void Area::clean(bool deleteShapes) { + myShapeDone = false; + mySections.clear(); myShape.Nullify(); - delete myArea; - myArea = NULL; - delete myAreaOpen; - myAreaOpen = NULL; + myArea.reset(); + myAreaOpen.reset(); if(deleteShapes){ myShapePlane.Nullify(); myShapes.clear(); myHaveFace = false; + myHaveSolid = false; } } void Area::add(const TopoDS_Shape &shape,short op) { -#define AREA_SRC_OP(_v) op - PARAM_ENUM_CONVERT(AREA_SRC_OP,,PARAM_ENUM_EXCEPT,AREA_PARAMS_OPCODE); +#define AREA_SRC_OP(_param) op + PARAM_ENUM_CONVERT(AREA_SRC_OP,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_OPCODE); + Q_UNUSED(Operation); + + 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(myShapes.empty()) - Operation = ClipperLib::ctUnion; - myShapes.push_back(Shape((short)Operation,shape)); + op = OperationUnion; + myShapes.push_back(Shape(op,shape)); } void Area::setParams(const AreaParams ¶ms) { -#define AREA_SRC2(_v) params._v +#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) + if(params!=myParams) { clean(); - myParams = params; + myParams = params; + } } void Area::addToBuild(CArea &area, const TopoDS_Shape &shape) { @@ -299,24 +319,28 @@ void Area::addToBuild(CArea &area, const TopoDS_Shape &shape) { plane = myWorkPlane.IsNull()?&myShapePlane:&myWorkPlane; CArea areaOpen; mySkippedShapes += add(area,shape,&myTrsf,myParams.Deflection,plane, - myParams.Coplanar==CoplanarForce,&areaOpen, + myHaveSolid||myParams.Coplanar==CoplanarForce,&areaOpen, myParams.OpenMode==OpenModeEdges,myParams.Reorder); if(areaOpen.m_curves.size()) { - if(&area == myArea || myParams.OpenMode == OpenModeNone) + if(&area == myArea.get() || myParams.OpenMode == OpenModeNone) myAreaOpen->m_curves.splice(myAreaOpen->m_curves.end(),areaOpen.m_curves); else Base::Console().Warning("open wires discarded in clipping shapes\n"); } } +namespace Part { +extern PartExport std::list sort_Edges(double tol3d, std::list& edges); +} + void Area::build() { - if(myArea) return; + if(myArea || mySections.size()) return; if(myShapes.empty()) throw Base::ValueError("Null shape"); -#define AREA_SRC(_v) myParams._v - PARAM_ENUM_CONVERT(AREA_SRC,,PARAM_ENUM_EXCEPT,AREA_PARAMS_CLIPPER_FILL); +#define AREA_SRC(_param) myParams.PARAM_FNAME(_param) + PARAM_ENUM_CONVERT(AREA_SRC,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_CLIPPER_FILL); if(myWorkPlane.IsNull()) { myShapePlane.Nullify(); @@ -350,36 +374,139 @@ void Area::build() { throw Base::ValueError("shapes are not planar"); } + if(myHaveSolid && myParams.SectionCount) { + + if(myParams.SectionOffset < 0) + throw Base::ValueError("invalid section offset"); + if(myParams.SectionCount>1 && myParams.Stepdown zMax-zMin) + count = ceil((zMax-zMin)/myParams.Stepdown); + for(int i=0;i area(new Area(&myParams)); + area->setPlane(face); + for(const Shape &s : myShapes) { + BRep_Builder builder; + TopoDS_Compound comp; + builder.MakeCompound(comp); + for(TopExp_Explorer it(s.shape, TopAbs_SOLID); it.More(); it.Next()) { + BRepAlgoAPI_Section section(it.Current().Moved(loc),face); + if(!section.IsDone()) { + ++error; + continue; + } + const TopoDS_Shape &shape = section.Shape(); + if(shape.IsNull()) continue; + + Part::FaceMakerBullseye mkFace; + mkFace.setPlane(pln); + + std::list edges; + for(TopExp_Explorer it(shape, TopAbs_EDGE); it.More(); it.Next()) + edges.push_back(TopoDS::Edge(it.Current())); + bool open = false; + std::list wires; + while(edges.size()) { + const std::list sorted = + Part::sort_Edges(Precision::Confusion(),edges); + BRepBuilderAPI_MakeWire mkWire; + for(const TopoDS_Edge &e : sorted) + mkWire.Add(e); + const TopoDS_Wire &wire = mkWire.Wire(); + if(!BRep_Tool::IsClosed(wire)) + open = true; + wires.push_back(wire); + } + if(!open) { + for(const TopoDS_Wire &wire : wires) + mkFace.addWire(wire); + try { + mkFace.Build(); + if (mkFace.Shape().IsNull()) + continue; + builder.Add(comp,mkFace.Shape()); + continue; + }catch (Base::Exception &e){ + Base::Console().Warning("FaceMakerBullseye failed: %s\n", e.what()); + } + } + //Shouldn't have any open wire, so count as error + ++error; + for(const TopoDS_Wire &wire : wires) + builder.Add(comp,wire); + } + if(comp.IsNull()) continue; + area->add(comp,s.op); + } + mySections.push_back(area); + } + if(error) + Base::Console().Warning("Some errors occured during operation\n"); + return; + } + try { - myArea = new CArea(); - myAreaOpen = new CArea(); + myArea.reset(new CArea()); + myAreaOpen.reset(new CArea()); CAreaConfig conf(myParams); CArea areaClip; mySkippedShapes = 0; - short op = ClipperLib::ctUnion; + short op = OperationUnion; bool pending = false; for(const Shape &s : myShapes) { if(op!=s.op) { if(myParams.OpenMode!=OpenModeNone) myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves); pending = false; - myArea->Clip((ClipperLib::ClipType)op,&areaClip,SubjectFill,ClipFill); + PARAM_ENUM_CONVERT(AREA_SRC_OP,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_OPCODE); + myArea->Clip(Operation,&areaClip,SubjectFill,ClipFill); areaClip.m_curves.clear(); op=s.op; } - addToBuild(op==ClipperLib::ctUnion?*myArea:areaClip,s.shape); + addToBuild(op==OperationUnion?*myArea:areaClip,s.shape); pending = true; } - if(mySkippedShapes) + if(mySkippedShapes && !myHaveSolid) Base::Console().Warning("%s %d non coplanar shapes\n", myParams.Coplanar==CoplanarForce?"Skipped":"Found",mySkippedShapes); if(pending){ if(myParams.OpenMode!=OpenModeNone) myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves); - myArea->Clip((ClipperLib::ClipType)op,&areaClip,SubjectFill,ClipFill); + PARAM_ENUM_CONVERT(AREA_SRC_OP,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_OPCODE); + myArea->Clip(Operation,&areaClip,SubjectFill,ClipFill); } myArea->m_curves.splice(myArea->m_curves.end(),myAreaOpen->m_curves); @@ -403,7 +530,7 @@ TopoDS_Shape Area::toShape(CArea &area, short fill) { bFill = false; } if(myParams.FitArcs) { - if(&area == myArea) { + if(&area == myArea.get()) { CArea copy(area); copy.FitArcs(); return toShape(copy,bFill,&trsf); @@ -413,47 +540,132 @@ TopoDS_Shape Area::toShape(CArea &area, short fill) { return toShape(area,bFill,&trsf); } -const TopoDS_Shape &Area::getShape() { - if(myShape.IsNull()) { - build(); - CAreaConfig conf(myParams); - myShape = toShape(*myArea,myParams.Fill); - } - return myShape; -} +#define AREA_SECTION(_op,_index,...) do {\ + if(mySections.size()) {\ + if(_index>=(int)mySections.size())\ + throw Base::ValueError("index out of bound");\ + TopLoc_Location loc(myTrsf.Inverted());\ + if(_index<0) {\ + BRep_Builder builder;\ + TopoDS_Compound compound;\ + builder.MakeCompound(compound);\ + for(shared_ptr area : mySections)\ + builder.Add(compound,area->_op(-1, ## __VA_ARGS__).Moved(loc));\ + return compound;\ + }\ + return mySections[_index]->_op(-1, ## __VA_ARGS__).Moved(loc);\ + }\ +}while(0) + +TopoDS_Shape Area::getShape(int index) { + build(); + AREA_SECTION(getShape,index); + + if(myShapeDone) return myShape; + + 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; + } + + // 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(); + } -TopoDS_Shape Area::makeOffset(PARAM_ARGS(ARG,AREA_PARAMS_OFFSET)) { - std::list shapes; - makeOffset(shapes,PARAM_FIELDS(ARG,AREA_PARAMS_OFFSET)); - if(shapes.empty()) - return TopoDS_Shape(); - if(shapes.size()==1) - return shapes.front(); BRep_Builder builder; TopoDS_Compound compound; builder.MakeCompound(compound); - for(const TopoDS_Shape &s : shapes) - builder.Add(compound,s); + + short fill = myParams.Thicken?FillFace:FillNone; + for(shared_ptr area : areas) { + if(myParams.Thicken) + area->Thicken(myParams.ToolRadius); + builder.Add(compound,toShape(*area,fill)); + } + builder.Add(compound,areaPocket.makePocket( + -1,PARAM_FIELDS(AREA_MY,AREA_PARAMS_POCKET))); + myShape = compound; + myShapeDone = true; + 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); + area.Thicken(myParams.ToolRadius); + return toShape(area,FillFace); + } + return TopoDS_Shape(); + } + BRep_Builder builder; + TopoDS_Compound compound; + builder.MakeCompound(compound); + for(shared_ptr area : areas) { + short fill; + if(myParams.Thicken && myParams.ToolRadius>Precision::Confusion()) { + area->Thicken(myParams.ToolRadius); + fill = FillFace; + }else if(areas.size()==1) + fill = myParams.Fill; + else + fill = FillNone; + builder.Add(compound,toShape(*area,fill)); + } return compound; } -void Area::makeOffset(std::list &shapes, - PARAM_ARGS(ARG,AREA_PARAMS_OFFSET)) +void Area::makeOffset(list > &areas, + PARAM_ARGS(PARAM_FARG,AREA_PARAMS_OFFSET)) { - if(fabs(offset) &shapes, } } - PARAM_ENUM_CONVERT(AREA_SRC,,PARAM_ENUM_EXCEPT,AREA_PARAMS_OFFSET_CONF); + PARAM_ENUM_CONVERT(AREA_SRC,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_OFFSET_CONF); #ifdef AREA_OFFSET_ALGO - PARAM_ENUM_CONVERT(AREA_SRC,,PARAM_ENUM_EXCEPT,AREA_PARAMS_CLIPPER_FILL); + 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) { @@ -512,16 +725,10 @@ void Area::makeOffset(std::list &shapes, if(area.m_curves.empty()) return; - - if(count == 1) { - shapes.push_back(toShape(area,myParams.Fill)); - return; - } - shapes.push_back(toShape(area,Area::FillNone)); } } -TopoDS_Shape Area::makePocket(PARAM_ARGS(ARG,AREA_PARAMS_POCKET)) { +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"); @@ -534,6 +741,9 @@ TopoDS_Shape Area::makePocket(PARAM_ARGS(ARG,AREA_PARAMS_POCKET)) { if(mode == Area::PocketModeNone) return TopoDS_Shape(); + build(); + AREA_SECTION(makePocket,index,PARAM_FIELDS(PARAM_FARG,AREA_PARAMS_POCKET)); + PocketMode pm; switch(mode) { case Area::PocketModeZigZag: @@ -543,11 +753,11 @@ TopoDS_Shape Area::makePocket(PARAM_ARGS(ARG,AREA_PARAMS_POCKET)) { pm = SpiralPocketMode; break; case Area::PocketModeOffset: { - PARAM_DECLARE_INIT(NAME,AREA_PARAMS_OFFSET); + PARAM_DECLARE_INIT(PARAM_FNAME,AREA_PARAMS_OFFSET); Offset = -tool_radius-extra_offset; ExtraPass = -1; Stepover = -stepover; - return makeOffset(PARAM_FIELDS(NAME,AREA_PARAMS_OFFSET)); + return makeOffset(index,PARAM_FIELDS(PARAM_FNAME,AREA_PARAMS_OFFSET)); }case Area::PocketModeZigZagOffset: pm = ZigZagThenSingleOffsetPocketMode; break; @@ -564,7 +774,12 @@ TopoDS_Shape Area::makePocket(PARAM_ARGS(ARG,AREA_PARAMS_POCKET)) { // reorder before input, otherwise nothing is shown. in.Reorder(); in.MakePocketToolpath(out.m_curves,params); - return toShape(out,FillNone); + + 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) { diff --git a/src/Mod/Path/App/Area.h b/src/Mod/Path/App/Area.h index 78b89aa01..02b4664bd 100644 --- a/src/Mod/Path/App/Area.h +++ b/src/Mod/Path/App/Area.h @@ -37,15 +37,14 @@ namespace Path /** Store libarea algorithm configuration */ struct PathExport CAreaParams { - PARAM_DECLARE(NAME,AREA_PARAMS_CAREA) + PARAM_DECLARE(PARAM_FNAME,AREA_PARAMS_CAREA) CAreaParams(); }; /** Store all Area configurations */ struct PathExport AreaParams: CAreaParams { - PARAM_DECLARE(NAME,AREA_PARAMS_BASE) - PARAM_DECLARE(NAME,AREA_PARAMS_OFFSET_CONF) + PARAM_DECLARE(PARAM_FNAME,AREA_PARAMS_AREA) bool operator==(const AreaParams &other) const { #define AREA_COMPARE(_param) \ @@ -68,7 +67,7 @@ struct PathExport AreaParams: CAreaParams { struct PathExport CAreaConfig { /** Stores current libarea settings */ - PARAM_DECLARE(NAME,AREA_PARAMS_CAREA) + PARAM_DECLARE(PARAM_FNAME,AREA_PARAMS_CAREA) /** Stores user defined setting */ CAreaParams params; @@ -105,14 +104,17 @@ protected: }; std::list myShapes; - CArea *myArea; - CArea *myAreaOpen; + std::unique_ptr myArea; + std::unique_ptr myAreaOpen; gp_Trsf myTrsf; AreaParams myParams; TopoDS_Shape myShapePlane; TopoDS_Shape myWorkPlane; TopoDS_Shape myShape; + std::vector > mySections; bool myHaveFace; + bool myHaveSolid; + bool myShapeDone; int mySkippedShapes; /** Called internally to combine children shapes for further processing */ @@ -126,7 +128,19 @@ protected: /** Called internally to obtain the combained children shapes */ TopoDS_Shape toShape(CArea &area, short fill); -public: + /** Obtain a list of offseted areas + * + * See #AREA_PARAMS_OFFSET for description of the arguments. + */ + void makeOffset(std::list > &areas, + PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_OFFSET)); + + /** Make a pocket of the combined shape + * + * User #AREA_PARAMS_POCKET setting in myParams. + */ + TopoDS_Shape makePocket(); + /** Declare all parameters defined in #AREA_PARAMS_ALL as member variable */ PARAM_ENUM_DECLARE(AREA_PARAMS_ALL) @@ -153,7 +167,8 @@ public: * \arg \c shape: the child shape * \arg \c op: operation code, see #AREA_PARAMS_OPCODE */ - void add(const TopoDS_Shape &shape,PARAM_ARGS_DEF(ARG,AREA_PARAMS_OPCODE)); + void add(const TopoDS_Shape &shape,PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_OPCODE)); + /** Generate an offset of the combined shape * @@ -161,20 +176,13 @@ public: * If more than one offset is requested, a compound shape is return * containing all offset shapes as wires regardless of \c Fill setting. */ - TopoDS_Shape makeOffset(PARAM_ARGS_DEF(ARG,AREA_PARAMS_OFFSET)); - - /** Obtain a list of offset shapes of the combined shape, - * - * See #AREA_PARAMS_OFFSET for description of the arguments. - */ - void makeOffset(std::list &shapes, - PARAM_ARGS_DEF(ARG,AREA_PARAMS_OFFSET)); + TopoDS_Shape makeOffset(int index, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_OFFSET)); /** Make a pocket of the combined shape * * See #AREA_PARAMS_POCKET for description of the arguments. */ - TopoDS_Shape makePocket(PARAM_ARGS_DEF(ARG,AREA_PARAMS_POCKET)); + TopoDS_Shape makePocket(int index, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_POCKET)); /** Config this Area object */ @@ -196,7 +204,7 @@ public: void clean(bool deleteShapes=false); /** Get the combined shape */ - const TopoDS_Shape &getShape(); + TopoDS_Shape getShape(int index); /** Add a OCC wire shape to CArea * diff --git a/src/Mod/Path/App/AreaParams.h b/src/Mod/Path/App/AreaParams.h index 8d16623de..80f8e325f 100644 --- a/src/Mod/Path/App/AreaParams.h +++ b/src/Mod/Path/App/AreaParams.h @@ -86,6 +86,9 @@ ((bool,from_center,FromCenter,true,"Start pocketing from center"))\ ((double,zig_angle,ZigAngle,45,"Zig angle in degree")) +#define AREA_PARAMS_POCKET_CONF \ + ((bool,thicken,Thicken,false,"Thicken the resulting wires with ToolRadius")) + /** Operation code */ #define AREA_PARAMS_OPCODE \ ((enum2,op,Operation,0,"Boolean operation",\ @@ -97,6 +100,12 @@ ((long,extra_pass,ExtraPass,0,"Number of extra offset pass to generate."))\ ((double,stepover,Stepover,0.0,"Cutter diameter to step over on each pass. If =0, use Offset")) +/** Section parameters */ +#define AREA_PARAMS_SECTION \ + ((long,count,SectionCount,0,"Number of sections to generate. -1 means full sections."))\ + ((double,stepdown,Stepdown,1.0,"Step down distance for each section"))\ + ((double,offset,SectionOffset,0.0,"Offset for the first section")) + #ifdef AREA_OFFSET_ALGO # define AREA_PARAMS_OFFSET_ALGO \ ((enum,algo,Algo,0,"Offset algorithm type",(Clipper)(libarea))) @@ -107,7 +116,6 @@ /** Offset configuration parameters */ #define AREA_PARAMS_OFFSET_CONF \ AREA_PARAMS_OFFSET_ALGO \ - ((bool,thicken,Thicken,false,"Thicken the resulting wires with Offset"))\ ((enum2,join_type,JoinType,0,"ClipperOffset join type. \nSee https://goo.gl/4odfQh",\ (Round)(Square)(Miter),(ClipperLib::JoinType,ClipperLib::jt)))\ ((enum2,end_type,EndType,0,"\nClipperOffset end type. See https://goo.gl/tj7gkX",\ @@ -116,17 +124,23 @@ ((double,round_precision,RoundPreceision,0.0,\ "Round joint precision. If =0, it defaults to Accuracy. \nSee https://goo.gl/4odfQh")) +/** Group of all Area configuration parameters except CArea's*/ +#define AREA_PARAMS_AREA \ + AREA_PARAMS_BASE \ + AREA_PARAMS_OFFSET \ + AREA_PARAMS_OFFSET_CONF \ + AREA_PARAMS_POCKET \ + AREA_PARAMS_POCKET_CONF \ + AREA_PARAMS_SECTION + /** Group of all Area configuration parameters */ #define AREA_PARAMS_CONF \ AREA_PARAMS_CAREA \ - AREA_PARAMS_BASE \ - AREA_PARAMS_OFFSET_CONF + AREA_PARAMS_AREA /** Group of all Area parameters */ #define AREA_PARAMS_ALL \ AREA_PARAMS_CONF \ - AREA_PARAMS_OPCODE \ - AREA_PARAMS_OFFSET \ - AREA_PARAMS_POCKET + AREA_PARAMS_OPCODE #endif //PATH_AreaParam_H diff --git a/src/Mod/Path/App/AreaPy.xml b/src/Mod/Path/App/AreaPy.xml index e4952e13b..6bc8cd2ec 100644 --- a/src/Mod/Path/App/AreaPy.xml +++ b/src/Mod/Path/App/AreaPy.xml @@ -26,9 +26,11 @@ any operation - + - toShape(rebuild=False): Return the resulting shape + getShape(index=-1,rebuild=False): Return the resulting shape\n +\n* index (-1): the index of the section. -1 means all sections. No effect on planar shape.\n +\n* rebuild: clean the internal cache and rebuild diff --git a/src/Mod/Path/App/AreaPyImp.cpp b/src/Mod/Path/App/AreaPyImp.cpp index 227740519..7c8b05851 100644 --- a/src/Mod/Path/App/AreaPyImp.cpp +++ b/src/Mod/Path/App/AreaPyImp.cpp @@ -65,6 +65,7 @@ static const AreaDoc myDocs[] = { "makeOffset", "makeOffset(" PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_OFFSET) "):\n" + "\n* index (-1): the index of the section. -1 means all sections. No effect on planar shape.\n" "Make an 2D offset of the shape.\n" PARAM_PY_DOC(ARG,AREA_PARAMS_OFFSET), }, @@ -73,6 +74,7 @@ static const AreaDoc myDocs[] = { "makePocket(" PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_POCKET) "):\n" "Generate pocket toolpath of the shape.\n" + "\n* index (-1): the index of the section. -1 means all sections. No effect on planar shape.\n" PARAM_PY_DOC(ARG,AREA_PARAMS_POCKET), }, }; @@ -94,7 +96,7 @@ struct AreaPyDoc { static AreaPyDoc doc; namespace Part { - extern Py::Object shape2pyshape(const TopoDS_Shape &shape); +extern PartExport Py::Object shape2pyshape(const TopoDS_Shape &shape); } using namespace Path; @@ -128,29 +130,30 @@ PyObject* AreaPy::setPlane(PyObject *args) { return Py_None; } -PyObject* AreaPy::toShape(PyObject *args, PyObject *keywds) +PyObject* AreaPy::getShape(PyObject *args, PyObject *keywds) { PyObject *pcObj = Py_True; - static char *kwlist[] = {"rebuild", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds,"|O",kwlist,&pcObj)) + short index=-1; + static char *kwlist[] = {"index","rebuild", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds,"|hO",kwlist,&pcObj)) Py_Error(Base::BaseExceptionFreeCADError, "This method accepts no argument"); try { if(PyObject_IsTrue(pcObj)) getAreaPtr()->clean(true); - return Py::new_reference_to(Part::shape2pyshape(getAreaPtr()->getShape())); + return Py::new_reference_to(Part::shape2pyshape(getAreaPtr()->getShape(index))); } PY_CATCH_OCC; } PyObject* AreaPy::add(PyObject *args, PyObject *keywds) { - PARAM_PY_DECLARE_INIT(ARG,AREA_PARAMS_OPCODE) + PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_OPCODE) PyObject *pcObj; static char *kwlist[] = {PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_OPCODE), NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|" PARAM_PY_KWDS(AREA_PARAMS_OPCODE), - kwlist,&pcObj,PARAM_REF(ARG,AREA_PARAMS_OPCODE))) + kwlist,&pcObj,PARAM_REF(PARAM_FARG,AREA_PARAMS_OPCODE))) Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters"); if (PyObject_TypeCheck(pcObj, &(Part::TopoShapePy::Type))) { @@ -168,7 +171,7 @@ PyObject* AreaPy::add(PyObject *args, PyObject *keywds) for (Py::Sequence::iterator it = shapeSeq.begin(); it != shapeSeq.end(); ++it){ PyObject* item = (*it).ptr(); getAreaPtr()->add(GET_TOPOSHAPE(item), - PARAM_PY_FIELDS(ARG,AREA_PARAMS_OPCODE)); + PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_OPCODE)); } return Py_None; } @@ -176,22 +179,23 @@ PyObject* AreaPy::add(PyObject *args, PyObject *keywds) PyObject* AreaPy::makeOffset(PyObject *args, PyObject *keywds) { //Generate a keyword string defined in the ARG field of OFFSET parameter list - static char *kwlist[] = {PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_OFFSET), NULL}; + static char *kwlist[] = {"index",PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_OFFSET), NULL}; + short index = -1; //Declare variables defined in the ARG field of the OFFSET parameter list with //initialization to defaults - PARAM_PY_DECLARE_INIT(ARG,AREA_PARAMS_OFFSET) + PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_OFFSET) //Parse arguments to overwrite the defaults if (!PyArg_ParseTupleAndKeywords(args, keywds, - "|" PARAM_PY_KWDS(AREA_PARAMS_OFFSET), kwlist, - PARAM_REF(ARG,AREA_PARAMS_OFFSET))) + "|h" PARAM_PY_KWDS(AREA_PARAMS_OFFSET), kwlist, + &index,PARAM_REF(PARAM_FARG,AREA_PARAMS_OFFSET))) Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters"); try { //Expand the variable as function call arguments - TopoDS_Shape resultShape = getAreaPtr()->makeOffset( - PARAM_PY_FIELDS(ARG,AREA_PARAMS_OFFSET)); + TopoDS_Shape resultShape = getAreaPtr()->makeOffset(index, + PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_OFFSET)); return Py::new_reference_to(Part::shape2pyshape(resultShape)); } PY_CATCH_OCC; @@ -199,18 +203,19 @@ PyObject* AreaPy::makeOffset(PyObject *args, PyObject *keywds) PyObject* AreaPy::makePocket(PyObject *args, PyObject *keywds) { - static char *kwlist[] = {PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_POCKET), NULL}; + static char *kwlist[] = {"index",PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_POCKET), NULL}; + short index = -1; - PARAM_PY_DECLARE_INIT(ARG,AREA_PARAMS_POCKET) + PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_POCKET) if (!PyArg_ParseTupleAndKeywords(args, keywds, - "|" PARAM_PY_KWDS(AREA_PARAMS_POCKET), kwlist, - PARAM_REF(ARG,AREA_PARAMS_POCKET))) + "|h" PARAM_PY_KWDS(AREA_PARAMS_POCKET), kwlist, + &index,PARAM_REF(PARAM_FARG,AREA_PARAMS_POCKET))) Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters"); try { - TopoDS_Shape resultShape = getAreaPtr()->makePocket( - PARAM_PY_FIELDS(ARG,AREA_PARAMS_POCKET)); + TopoDS_Shape resultShape = getAreaPtr()->makePocket(index, + PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_POCKET)); return Py::new_reference_to(Part::shape2pyshape(resultShape)); } PY_CATCH_OCC; @@ -222,7 +227,7 @@ PyObject* AreaPy::setParams(PyObject *args, PyObject *keywds) static char *kwlist[] = {PARAM_FIELD_STRINGS(NAME,AREA_PARAMS_CONF),NULL}; //Declare variables defined in the NAME field of the CONF parameter list - PARAM_PY_DECLARE(NAME,AREA_PARAMS_CONF); + PARAM_PY_DECLARE(PARAM_FNAME,AREA_PARAMS_CONF); AreaParams params = getAreaPtr()->getParams(); @@ -235,7 +240,7 @@ PyObject* AreaPy::setParams(PyObject *args, PyObject *keywds) //Parse arguments to overwrite CONF variables if (!PyArg_ParseTupleAndKeywords(args, keywds, "|" PARAM_PY_KWDS(AREA_PARAMS_CONF), kwlist, - PARAM_REF(NAME,AREA_PARAMS_CONF))) + PARAM_REF(PARAM_FNAME,AREA_PARAMS_CONF))) Py_Error(Base::BaseExceptionFreeCADError, "Wrong parameters, call getParamsDesc() to get supported params"); @@ -257,8 +262,8 @@ PyObject* AreaPy::getParams(PyObject *args) const AreaParams ¶ms =getAreaPtr()->getParams(); PyObject *dict = PyDict_New(); -#define AREA_SRC(_v) params._v - PARAM_PY_DICT_SET_VALUE(dict,AREA_SRC,AREA_PARAMS_CONF) +#define AREA_SRC(_param) params.PARAM_FNAME(_param) + PARAM_PY_DICT_SET_VALUE(dict,NAME,AREA_SRC,AREA_PARAMS_CONF) return dict; } @@ -273,7 +278,7 @@ PyObject* AreaPy::getParamsDesc(PyObject *args, PyObject *keywds) return PyString_FromString(PARAM_PY_DOC(NAME,AREA_PARAMS_CONF)); PyObject *dict = PyDict_New(); - PARAM_PY_DICT_SET_DOC(dict,AREA_PARAMS_CONF) + PARAM_PY_DICT_SET_DOC(dict,NAME,AREA_PARAMS_CONF) return dict; } diff --git a/src/Mod/Path/App/FeatureArea.cpp b/src/Mod/Path/App/FeatureArea.cpp index cdc26f1d7..b02d23441 100644 --- a/src/Mod/Path/App/FeatureArea.cpp +++ b/src/Mod/Path/App/FeatureArea.cpp @@ -48,6 +48,8 @@ FeatureArea::FeatureArea() PARAM_PROP_ADD("Area",AREA_PARAMS_BASE); PARAM_PROP_ADD("Offset",AREA_PARAMS_OFFSET); PARAM_PROP_ADD("Pocket",AREA_PARAMS_POCKET); + PARAM_PROP_ADD("Pocket",AREA_PARAMS_POCKET_CONF); + PARAM_PROP_ADD("Section",AREA_PARAMS_SECTION); PARAM_PROP_ADD("Offset Settings", AREA_PARAMS_OFFSET_CONF); PARAM_PROP_ADD("libarea Settings",AREA_PARAMS_CAREA); @@ -77,7 +79,6 @@ App::DocumentObjectExecReturn *FeatureArea::execute(void) #define AREA_PROP_GET(_param) \ params.PARAM_FNAME(_param) = PARAM_FNAME(_param).getValue(); - PARAM_FOREACH(AREA_PROP_GET,AREA_PARAMS_CONF) Area area(¶ms); @@ -86,45 +87,12 @@ App::DocumentObjectExecReturn *FeatureArea::execute(void) if(!workPlane.IsNull()) area.setPlane(workPlane); - area.clean(true); for (std::vector::iterator it = links.begin(); it != links.end(); ++it) { area.add(static_cast(*it)->Shape.getShape().getShape(), PARAM_PROP_ARGS(AREA_PARAMS_OPCODE)); } - std::list shapes; - if(fabs(Offset.getValue())>Precision::Confusion()) - area.makeOffset(shapes,PARAM_PROP_ARGS(AREA_PARAMS_OFFSET)); - - if(PocketMode.getValue()) { - Area areaPocket(¶ms); - if(shapes.empty()) - areaPocket.add(area.getShape()); - else{ - bool front = true; - if(shapes.size()>1) { - double step = Stepover.getValue(); - if(fabs(step)0; - } - areaPocket.add(front?shapes.front():shapes.back()); - } - shapes.push_back(areaPocket.makePocket(PARAM_PROP_ARGS(AREA_PARAMS_POCKET))); - } - - if(shapes.empty()) - this->Shape.setValue(area.getShape()); - else if(shapes.size()==1) - this->Shape.setValue(shapes.front()); - else { - BRep_Builder builder; - TopoDS_Compound compound; - builder.MakeCompound(compound); - for(const TopoDS_Shape &s : shapes) - builder.Add(compound,s); - this->Shape.setValue(compound); - } + this->Shape.setValue(area.getShape(-1)); return Part::Feature::execute(); } diff --git a/src/Mod/Path/App/ParamsHelper.h b/src/Mod/Path/App/ParamsHelper.h index f0d65dd96..0d59116ba 100644 --- a/src/Mod/Path/App/ParamsHelper.h +++ b/src/Mod/Path/App/ParamsHelper.h @@ -315,42 +315,52 @@ /** Helper for #PARAM_DECLARE */ -#define PARAM_DECLARE_(_1,_field,_param) \ - PARAM_TYPE(_param) PARAM_FIELD(_field,_param); +#define PARAM_DECLARE_(_1,_src,_param) \ + PARAM_TYPE(_param) _src(_param); /** * Delcares parameters using the given field as name * - * \arg \c _field: specifies the \ref ParamField "field" to use as name + * \arg \c _src: \anchor ParamSrc Macro to generate source variable. The + * signature must be _src(_param)<\tt>, where \c _param is the tuple + * defining the parameter. You pass any of the \ref ParamAccessor "parameter + * accessors" to directly access the field. Or, supply your own macro to append + * any prefix as you like. For example: + * \code{.unparsed} + * #define MY_SRC(_param) BOOST_PP_CAT(my,PARAM_FNAME(_param)) + * -> + * my## + * \endcode * * Expands to: * \code{.unparsed} - * type1 _field1;type2 _field2; ... + * type1 _src(_param1);type2 _src(_param2); ... * \endcode * \ingroup ParamCommon */ -#define PARAM_DECLARE(_field,_seq) \ - BOOST_PP_SEQ_FOR_EACH(PARAM_DECLARE_,_field,_seq) +#define PARAM_DECLARE(_src,_seq) \ + BOOST_PP_SEQ_FOR_EACH(PARAM_DECLARE_,_src,_seq) /** Helper for #PARAM_DECLARE_INIT */ -#define PARAM_DECLARE_INIT_(_1,_field,_param) \ - PARAM_TYPE(_param) PARAM_FIELD(_field,_param) = PARAM_FDEF(_param); +#define PARAM_DECLARE_INIT_(_1,_src,_param) \ + PARAM_TYPE(_param) _src(_param) = PARAM_FDEF(_param); /** * Delcares parameters with initialization to default using the given field as * name * - * \arg \c _field: \ref ParamField "field" to use as name + * \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for + * more details * * Expands to: * \code{.unparsed} - * type1 _field1=_def1;type2 _field2=_def2; ... + * type1 _src(_param1)=_def1;type2 _src(_param2)=_def2; ... * \endcode * \ingroup ParamCommon */ -#define PARAM_DECLARE_INIT(_field,_seq) \ - BOOST_PP_SEQ_FOR_EACH(PARAM_DECLARE_INIT_,_field,_seq) +#define PARAM_DECLARE_INIT(_src,_seq) \ + BOOST_PP_SEQ_FOR_EACH(PARAM_DECLARE_INIT_,_src,_seq) #define PARAM_ENUM_DECLARE_enum_(_1,_name,_i,_elem) \ @@ -394,8 +404,7 @@ #define PARAM_ENUM_CONVERT_enum_(_dst,_name,_prefix,_elem) \ case BOOST_PP_CAT(_name,_elem):\ - _dst(_name) = \ - BOOST_PP_CAT(_prefix,_elem);\ + _dst = BOOST_PP_CAT(_prefix,_elem);\ break; #define PARAM_ENUM_CONVERT__(_1,_args,_i,_elem) \ @@ -418,10 +427,10 @@ * i.e. not double but single parathesis */ #define PARAM_ENUM_CONVERT_SINGLE(_src,_dst,_default,_param) \ - PARAM_FENUM_TYPE(_param) _dst(PARAM_FNAME(_param));\ - switch(_src(PARAM_FNAME(_param))) {\ + PARAM_FENUM_TYPE(_param) _dst(_param);\ + switch(_src(_param)) {\ BOOST_PP_SEQ_FOR_EACH_I(PARAM_ENUM_CONVERT__,\ - (_dst,PARAM_FNAME(_param),PARAM_FENUM_PREFIX(_param)),PARAM_FSEQ(_param))\ + (_dst(_param),PARAM_FNAME(_param),PARAM_FENUM_PREFIX(_param)),PARAM_FSEQ(_param))\ default: \ _default(_param);\ } @@ -441,11 +450,12 @@ * * \ingroup ParamEnumHelper * - * \arg \c _src: Optional macro to generate source variable. The signature must - * be _src(_name)<\tt>, where \c _name will be the parameters \a name field. - * In case you just want \c _name as the source variable name, you can simply - * omit this argument, because newer C++ preprocessor allows empty argument. - * \arg \c _dst: Optional macro to generate destination variable. Same as above. + * \arg \c _src: Macro to generate source variable. The signature must be + * _src(_param)<\tt>, where \c _param is the tuple defining the parameter. + * You pass any of the \ref ParamAccessor "parameter accessors" to directly + * access the field. Or, supply your own macro to append any prefix as you + * like. + * \arg \c _dst: Same as above. * \arg \c _default: A macro to call for invalid value. Signature should be * _default(_param)<\tt>, where \c _param is the parameter definition. You * can use #PARAM_ENUM_EXCEPT to throw Base::ValueError exception in FreeCAD @@ -457,12 +467,12 @@ * ((enum,test1,Test1,0,"it's a test",(Foo)(Bar),(MyEnum1,myEnum1)) \ * ((enum,test2,Test2,0,"it's a test",(Foo)(Bar),(MyEnum2,myEnum2))) * - * #define MY_DST(_v) BOOST_PP_CAT(my,_v) + * #define MY_DST(_param) BOOST_PP_CAT(my,PARAM_FNAME(_param)) * \code{.unparsed} * - * calling (note that the \c _src macro is omitted) + * calling * \code{.unparsed} - * PARAM_ENUM_CONVERT(,MY_DST,My,PARAM_ENUM_EXCEP,MY_PARAM_TEST) + * PARAM_ENUM_CONVERT(PARAM_FNAME,MY_DST,My,PARAM_ENUM_EXCEP,MY_PARAM_TEST) * \code{.unparsed} * * expands to @@ -514,7 +524,7 @@ _param) #define PARAM_ENUM_CHECK_SINGLE(_src,_default,_param) \ - switch(_src(PARAM_FNAME(_param))) {\ + switch(_src(_param)) {\ BOOST_PP_SEQ_FOR_EACH_I(PARAM_ENUM_CHECK_enum_,\ PARAM_FNAME(_param),PARAM_FSEQ(_param))\ default: \ @@ -528,10 +538,11 @@ * * \ingroup ParamEnumHelper * - * \arg \c _src: Optional macro to generate source variable. The signature must - * be _src(_name)<\tt>, where \c _name will be the parameters \a name field. - * In case you just want \c _name as the source variable name, you can simply - * omit this argument, because newer C++ preprocessor allows empty argument. + * \arg \c _src: Macro to generate source variable. The signature must be + * _src(_param)<\tt>, where \c _param is the tuple defining the parameter. + * You pass any of the \ref ParamAccessor "parameter accessors" to directly + * access the field. Or, supply your own macro to append any prefix as you + * like. * * \arg \c _default: A macro to call for invalid value. Signature should be * _default(_param)<\tt>, where \c _param is the parameter definition. You @@ -574,57 +585,86 @@ /** Helper for #PARAM_INIT */ -#define PARAM_INIT_(_,_field,_i,_param) \ - BOOST_PP_COMMA_IF(_i) PARAM_FIELD(_field,_param)(PARAM_FDEF(_param)) +#define PARAM_INIT_(_,_src,_i,_param) \ + BOOST_PP_COMMA_IF(_i) _src(_param)(PARAM_FDEF(_param)) /** Constructor initialization * - * \arg \c _field: specifies the \ref ParamField "field" to use as name + * \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for + * more details * * Expand to, * \code{.unparsed} - * field1(def1), field2(def2)... + * _src(_param1)(def1), _src(_param1)(def2)... * \endcode * \ingroup ParamCommon */ -#define PARAM_INIT(_field,_seq) \ - BOOST_PP_SEQ_FOR_EACH_I(PARAM_INIT_,_field,_seq) +#define PARAM_INIT(_src,_seq) \ + BOOST_PP_SEQ_FOR_EACH_I(PARAM_INIT_,_src,_seq) + + +/** Helper for #PARAM_OP */ +#define PARAM_OP_(_,_args,_param) \ + BOOST_PP_TUPLE_ELEM(0,_args)(_param) BOOST_PP_TUPLE_ELEM(1,_args) \ + BOOST_PP_TUPLE_ELEM(2,_args)(_param); + +/** Perform operation on two instance of each parameter in a sequence + * + * \arg \c _src: Macro to generate source variable. The signature must be + * _src(_param)<\tt>, where \c _param is the tuple defining the parameter. + * You pass any of the \ref ParamAccessor "parameter accessors" to directly + * access the field. Or, supply your own macro to append any prefix as you + * like. + * \arg \c _op: a boolean operator + * \arg \c _dst: Same as \c _src above. + * + * Expands to: + * \code{.unparsed} + * _src(_param1) _op _src(_param2); + * \endcode + * + * \ingroup ParamCommon + */ +#define PARAM_OP(_src,_op,_dst,_seq) \ + BOOST_PP_SEQ_FOR_EACH(PARAM_COPY_,(_src,_op,_dst),_seq) /** Helper for #PARAM_ARGS_DEF */ -#define PARAM_ARGS_DEF_(_,_field,_i,_param) \ - BOOST_PP_COMMA_IF(_i) PARAM_TYPE(_param) PARAM_FIELD(_field,_param)=PARAM_FDEF(_param) +#define PARAM_ARGS_DEF_(_,_src,_i,_param) \ + BOOST_PP_COMMA_IF(_i) PARAM_TYPE(_param) _src(_param)=PARAM_FDEF(_param) /** Delcare the parameters as function argument list with defaults. * - * \arg \c _field: specifies the \ref ParamField "field" to use as name + * \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for + * more details * * Expand to: * \code{.unparsed} - * type1 field1=def1, type2 field2=def2 ... + * type1 _src(_param1)=def1, type2 _src(_param1)=def2 ... * \endcode * \ingroup ParamCommon */ -#define PARAM_ARGS_DEF(_field,_seq) \ - BOOST_PP_SEQ_FOR_EACH_I(PARAM_ARGS_DEF_,_field,_seq) +#define PARAM_ARGS_DEF(_src,_seq) \ + BOOST_PP_SEQ_FOR_EACH_I(PARAM_ARGS_DEF_,_src,_seq) /** Helper for #PARAM_ARGS */ -#define PARAM_ARGS_(_,_field,_i,_param) \ - BOOST_PP_COMMA_IF(_i) PARAM_TYPE(_param) PARAM_FIELD(_field,_param) +#define PARAM_ARGS_(_,_src,_i,_param) \ + BOOST_PP_COMMA_IF(_i) PARAM_TYPE(_param) _src(_param) /** Delcare the parameters as function argument list without defaults. * - * \arg \c _field: specifies the \ref ParamField "field" to use as name + * \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for + * more details * * Expand to: * \code{.unparsed} - * type1 field1, type2 field2 ... + * type1 _src(_param1), type2 _src(_param2) ... * \endcode * \ingroup ParamCommon */ -#define PARAM_ARGS(_field,_seq) \ - BOOST_PP_SEQ_FOR_EACH_I(PARAM_ARGS_,_field,_seq) +#define PARAM_ARGS(_src,_seq) \ + BOOST_PP_SEQ_FOR_EACH_I(PARAM_ARGS_,_src,_seq) /** \defgroup ParamPy Python helper @@ -685,19 +725,22 @@ /** Helper for #PARAM_FIELDS */ -#define PARAM_FIELDS_(_1,_field,_i,_param) \ - BOOST_PP_COMMA_IF(_i) PARAM_FIELD(_field,_param) +#define PARAM_FIELDS_(_1,_src,_i,_param) \ + BOOST_PP_COMMA_IF(_i) _src(_param) /** Expand to a list of the given field in the parameter sequence * - * For example, PARAM_FIELDS(ARG, _seq) expands to: + * \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for + * more details + * + * For example, PARAM_FIELDS(PARAM_FARG, _seq) expands to: * \code{.unparsed} * arg1,arg2 ... * \endcode * \ingroup ParamCommon ParamPy */ -#define PARAM_FIELDS(_field,_seq) \ - BOOST_PP_SEQ_FOR_EACH_I(PARAM_FIELDS_,_field,_seq) +#define PARAM_FIELDS(_src,_seq) \ + BOOST_PP_SEQ_FOR_EACH_I(PARAM_FIELDS_,_src,_seq) #define PARAM_PY_CAST_short(_v) (_v) @@ -716,16 +759,19 @@ /** Helper for #PARAM_PY_FIELDS */ -#define PARAM_PY_FIELDS_(_1,_field,_i,_param) \ - BOOST_PP_COMMA_IF(_i) PARAM_TYPED(PARAM_CAST_PY_,_param)(PARAM_FIELD(_field,_param)) +#define PARAM_PY_FIELDS_(_1,_src,_i,_param) \ + BOOST_PP_COMMA_IF(_i) PARAM_TYPED(PARAM_CAST_PY_,_param)(_src(_param)) -/** Expand to a list of the given field in the sequence +/** Expand to a comma separated list of the given field in the sequence + * + * \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for + * more details * * The field will be casted from python C to C type * \ingroup ParamCommon ParamPy */ -#define PARAM_PY_FIELDS(_field,_seq) \ - BOOST_PP_SEQ_FOR_EACH_I(PARAM_PY_FIELDS_,_field,_seq) +#define PARAM_PY_FIELDS(_src,_seq) \ + BOOST_PP_SEQ_FOR_EACH_I(PARAM_PY_FIELDS_,_src,_seq) /** Helper for #PARAM_FIELD_STRINGS */ @@ -733,7 +779,7 @@ BOOST_PP_COMMA_IF(_i) PARAM_FIELD_STR(_field,_param) /** Expand to a list of stringified fields - * \ingroup ParamCommon ParamPy + * \ingroup ParamStringizer ParamPy */ #define PARAM_FIELD_STRINGS(_field,_seq) \ BOOST_PP_SEQ_FOR_EACH_I(PARAM_FIELD_STRINGS_,_field,_seq) @@ -764,14 +810,14 @@ #define PARAM_PY_TYPE_enum2 short /** Helper for #PARAM_PY_DECLARE */ -#define PARAM_PY_DECLARE_(_1,_field,_param) \ - PARAM_TYPED(PARAM_PY_TYPE_,_param) PARAM_FIELD(_field,_param); +#define PARAM_PY_DECLARE_(_1,_src,_param) \ + PARAM_TYPED(PARAM_PY_TYPE_,_param) _src(_param); /** Declare field variables for Python C type without initialization * \ingroup ParamPy */ -#define PARAM_PY_DECLARE(_field,_seq) \ - BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DECLARE_,_field,_seq) +#define PARAM_PY_DECLARE(_src,_seq) \ + BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DECLARE_,_src,_seq) #define PARAM_PY_INIT_short(_v) _v #define PARAM_PY_INIT_long(_v) _v @@ -781,31 +827,34 @@ #define PARAM_PY_INIT_enum2(_v) _v /** Helper for #PARAM_PY_DECLARE_INIT */ -#define PARAM_PY_DECLARE_INIT_(_1,_field,_param) \ - PARAM_TYPED(PARAM_PY_TYPE_,_param) PARAM_FIELD(_field,_param) = \ +#define PARAM_PY_DECLARE_INIT_(_1,_src,_param) \ + PARAM_TYPED(PARAM_PY_TYPE_,_param) _src(_param) = \ PARAM_TYPED(PARAM_PY_INIT_,_param)(PARAM_FDEF(_param)); /** Declare field variables of Python c type with initialization to default * \ingroup ParamPy */ -#define PARAM_PY_DECLARE_INIT(_field,_seq) \ - BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DECLARE_INIT_,_field,_seq) +#define PARAM_PY_DECLARE_INIT(_src,_seq) \ + BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DECLARE_INIT_,_src,_seq) /** Helper for #PARAM_REF */ -#define PARAM_REF_(_1,_field,_i,_param) \ - BOOST_PP_COMMA_IF(_i) &PARAM_FIELD(_field,_param) +#define PARAM_REF_(_1,_src,_i,_param) \ + BOOST_PP_COMMA_IF(_i) &_src(_param) /** Generate a list of field references * + * \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for + * + * more details * Expand to: * \code{.unparsed} - * &_field1, &_field2 ... + * &_src(_param1), &_src(_param1) ... * \endcode * \ingroup ParamPy */ -#define PARAM_REF(_field,_seq) \ - BOOST_PP_SEQ_FOR_EACH_I(PARAM_REF_,_field,_seq) +#define PARAM_REF(_src,_seq) \ + BOOST_PP_SEQ_FOR_EACH_I(PARAM_REF_,_src,_seq) #define PARAM_CAST_PYOBJ_short(_v) PyInt_FromLong(_v) @@ -816,35 +865,36 @@ #define PARAM_CAST_PYOBJ_enum2 PARAM_CAST_PYOBJ_short -/** Stringize field to a Python string */ -#define PARAM_PY_STRINGIZE(_field,_param) \ +/** Stringize field to a Python string + * \ingroup ParamPy ParamStringizer + */ +#define PARAM_PY_STR(_field,_param) \ PyString_FromString(PARAM_FIELD_STR(_field,_param)) /** Helper for #PARAM_PY_DICT_SET_VALUE */ #define PARAM_PY_DICT_SET_VALUE_(_1,_args,_param) \ PyDict_SetItem(BOOST_PP_TUPLE_ELEM(0,_args), \ - PARAM_PY_STRINGIZE(NAME,_param),\ + PARAM_PY_STR(BOOST_PP_TUPLE_ELEM(1,_args),_param),\ PARAM_TYPED(PARAM_CAST_PYOBJ_,_param)(\ - BOOST_PP_TUPLE_ELEM(1,_args)(PARAM_FIELD(NAME,_param)))); + BOOST_PP_TUPLE_ELEM(2,_args)(_param))); /** Populate a Python dict with a structure variable * * \arg \c _dict: the Python dictionary object - * \arg \c _src: Optional macro to generate source variable. The signature must - * be _src(_name)<\tt>, where \c _name will be the parameters \a name field. - * In case you just want \c _name as the source variable name, you can simply - * omit this argument, because newer C++ preprocessor allows empty argument. + * \arg \c _field: specifies the \ref ParamField "field" to use as key + * \arg \c _src: macro to generate source field. See \ref ParamSrc "here" for + * more details * * Roughly translated to: * \code{.unparsed} - * PyDict_SetItem(_dict,#name1,_src(name1)); - * PyDict_SetItem(_dict,#name2,_src(name2)); + * PyDict_SetItem(_dict,#_field1,_src(_param)); + * PyDict_SetItem(_dict,#_field2,_src(_param)); * ... * \endcode * \ingroup ParamPy */ -#define PARAM_PY_DICT_SET_VALUE(_dict,_src,_seq) \ - BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DICT_SET_VALUE_,(_dict,_src),_seq) +#define PARAM_PY_DICT_SET_VALUE(_dict,_field,_src,_seq) \ + BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DICT_SET_VALUE_,(_dict,_field,_src),_seq) #define PARAM_PY_DICT_DOC_enum_(_i,_elem) \ @@ -868,22 +918,26 @@ #define PARAM_PY_DICT_DOC_enum2 PARAM_PY_DICT_DOC_enum /** Helper for #PARAM_PY_DICT_SET_DOC */ -#define PARAM_PY_DICT_SET_DOC_(_1,_dict,_param) \ - PyDict_SetItem(_dict, PARAM_PY_STRINGIZE(NAME,_param),\ - PyString_FromString(PARAM_TYPED(PARAM_PY_DICT_DOC_,_param)(_param))); +#define PARAM_PY_DICT_SET_DOC_(_1,_args,_param) \ + PyDict_SetItem(BOOST_PP_TUPLE_ELEM(0,_args), \ + PARAM_PY_STR(BOOST_PP_TUPLE_ELEM(1,_args),_param),\ + PyString_FromString(PARAM_TYPED(PARAM_PY_DICT_DOC_,_param)(_param))); /** Populate a Python dict with the doc field of the parameter sequence + * + * \arg \c _dict: the Python dictionary object + * \arg \c _field: specifies the \ref ParamField "field" to use as key * * Roughly translated to: * \code{.unparsed} - * PyDict_SetItem(_dict,#name1,doc1); - * PyDict_SetItem(_dict,#name2,doc2); + * PyDict_SetItem(_dict,#_field1,doc1); + * PyDict_SetItem(_dict,#_field1,doc2); * ... * \endcode * \ingroup ParamDoc */ -#define PARAM_PY_DICT_SET_DOC(_dict,_seq) \ - BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DICT_SET_DOC_,_dict,_seq) +#define PARAM_PY_DICT_SET_DOC(_dict,_field,_seq) \ + BOOST_PP_SEQ_FOR_EACH(PARAM_PY_DICT_SET_DOC_,(_dict,_field),_seq) /** \defgroup ParamProperty Property Macros diff --git a/src/Mod/Path/Gui/Command.cpp b/src/Mod/Path/Gui/Command.cpp index ab0a8296d..07528e0c9 100644 --- a/src/Mod/Path/Gui/Command.cpp +++ b/src/Mod/Path/Gui/Command.cpp @@ -66,44 +66,37 @@ CmdPathArea::CmdPathArea() void CmdPathArea::activated(int iMsg) { Q_UNUSED(iMsg); - std::vector Sel = - getSelection().getSelectionEx(NULL, Part::Feature::getClassTypeId()); std::list cmds; std::ostringstream sources; - if (Sel.size() > 0) { - for(const Gui::SelectionObject &selObj : Sel) { - const Part::Feature *pcObj = static_cast(selObj.getObject()); - if(selObj.getSubNames().empty()) { - const TopoDS_Shape &shape = pcObj->Shape.getShape().getShape(); - TopExp_Explorer it(shape, TopAbs_SHELL); - if(it.More()) { - Base::Console().Error("Selected shape is not 2D\n"); - return; - } - sources << "FreeCAD.activeDocument()." << pcObj->getNameInDocument() << ","; - continue; + for(const Gui::SelectionObject &selObj : + getSelection().getSelectionEx(NULL, Part::Feature::getClassTypeId())) + { + const Part::Feature *pcObj = static_cast(selObj.getObject()); + const std::vector &subnames = selObj.getSubNames(); + if(subnames.empty()) { + sources << "FreeCAD.activeDocument()." << pcObj->getNameInDocument() << ","; + continue; + } + for(const std::string &name : subnames) { + if(!name.compare(0,4,"Face") && + !name.compare(0,4,"Edge")) + { + Base::Console().Error("Selected shape is not 2D\n"); + return; } - for(const std::string &name : selObj.getSubNames()) { - if(!name.compare(0,4,"Face") && - !name.compare(0,4,"Edge")) - { - Base::Console().Error("Selected shape is not 2D\n"); - return; - } + int index = atoi(name.substr(4).c_str()); - int index = atoi(name.substr(4).c_str()); + std::ostringstream subname; + subname << pcObj->getNameInDocument() << '_' << name; + std::string sub_fname = getUniqueObjectName(subname.str().c_str()); - std::ostringstream subname; - subname << pcObj->getNameInDocument() << '_' << name; - std::string sub_fname = getUniqueObjectName(subname.str().c_str()); - - std::ostringstream cmd; - cmd << "FreeCAD.activeDocument().addObject('Path::Feature','" << sub_fname << "').Shape = " << - pcObj->getNameInDocument() << '.' << name.substr(0,4) << '[' << index-1 << ']'; - cmds.push_back(cmd.str()); - sources << "FreeCAD.activeDocument()." << sub_fname << ","; - } + std::ostringstream cmd; + cmd << "FreeCAD.activeDocument().addObject('Part::Feature','" << sub_fname << + "').Shape = FreeCAD.activeDocument()." << pcObj->getNameInDocument() << ".Shape." << + name.substr(0,4) << "s[" << index-1 << ']'; + cmds.push_back(cmd.str()); + sources << "FreeCAD.activeDocument()." << sub_fname << ","; } } std::string FeatName = getUniqueObjectName("FeatureArea"); @@ -121,6 +114,101 @@ bool CmdPathArea::isActive(void) return hasActiveDocument(); } + +DEF_STD_CMD_A(CmdPathAreaWorkplane) + +CmdPathAreaWorkplane::CmdPathAreaWorkplane() + :Command("Path_Area_Workplane") +{ + sAppModule = "Path"; + sGroup = QT_TR_NOOP("Path"); + sMenuText = QT_TR_NOOP("Area workplane"); + sToolTipText = QT_TR_NOOP("Select a workplane for a FeatureArea"); + sWhatsThis = "Path_Area_Workplane"; + sStatusTip = sToolTipText; + sPixmap = "Path-Area-Workplane"; + sAccel = "P,W"; + +} + +void CmdPathAreaWorkplane::activated(int iMsg) +{ + Q_UNUSED(iMsg); + + std::string areaName; + std::string planeSubname; + std::string planeName; + + for(Gui::SelectionObject &selObj : + getSelection().getSelectionEx(NULL, Part::Feature::getClassTypeId())) + { + const std::vector &subnames = selObj.getSubNames(); + if(subnames.size()>1) { + Base::Console().Error("Please select one sub shape object for plane only\n"); + return; + } + const Part::Feature *pcObj = static_cast(selObj.getObject()); + if(subnames.empty()) { + if(pcObj->getTypeId().isDerivedFrom(Path::FeatureArea::getClassTypeId())) { + if(areaName.size()){ + Base::Console().Error("Please select one FeatureArea only\n"); + return; + } + areaName = pcObj->getNameInDocument(); + continue; + } + for (TopExp_Explorer it(pcObj->Shape.getShape().getShape(), TopAbs_SHELL); it.More(); it.Next()) { + Base::Console().Error("Selected shape is not 2D\n"); + return; + } + } + if(planeName.size()){ + Base::Console().Error("Please select one shape object for plane only\n"); + return; + }else{ + planeSubname = planeName = pcObj->getNameInDocument(); + planeSubname += ".Shape"; + } + + for(const std::string &name : subnames) { + if(!name.compare(0,4,"Face") && + !name.compare(0,4,"Edge")) + { + Base::Console().Error("Selected shape is not 2D\n"); + return; + } + + int index = atoi(name.substr(4).c_str()); + + std::ostringstream subname; + subname << planeSubname << '.' << name.substr(0,4) << "s[" << index-1 << ']'; + planeSubname = subname.str(); + } + } + if(areaName.empty()) { + Base::Console().Error("Please select one FeatureArea\n"); + return; + } + if(planeName.empty()) { + Base::Console().Error("Please select one shape object\n"); + return; + } + + openCommand("Select Workplane for Path Area"); + doCommand(Doc,"FreeCAD.activeDocument().%s.WorkPlane = FreeCAD.activeDocument().%s", + areaName.c_str(),planeSubname.c_str()); + doCommand(Doc,"FreeCAD.activeDocument().%s.ViewObject.Visibility = True",areaName.c_str()); + // doCommand(Doc,"FreeCAD.activeDocument().%s.ViewObject.Visibility = False",planeName.c_str()); + commitCommand(); + updateActive(); +} + +bool CmdPathAreaWorkplane::isActive(void) +{ + return !getSelection().getSelectionEx(NULL, Path::FeatureArea::getClassTypeId()).empty(); +} + + // Path compound ##################################################################################################### @@ -229,4 +317,5 @@ void CreatePathCommands(void) rcCmdMgr.addCommand(new CmdPathCompound()); rcCmdMgr.addCommand(new CmdPathShape()); rcCmdMgr.addCommand(new CmdPathArea()); + rcCmdMgr.addCommand(new CmdPathAreaWorkplane()); } diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc index 23035a803..4ad635018 100644 --- a/src/Mod/Path/Gui/Resources/Path.qrc +++ b/src/Mod/Path/Gui/Resources/Path.qrc @@ -47,6 +47,7 @@ icons/Path-Toolpath.svg icons/Path-ToolTable.svg icons/Path-Area.svg + icons/Path-Area-Workplane.svg icons/preferences-path.svg panels/ContourEdit.ui panels/DlgJobChooser.ui diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Area-Workplane.svg b/src/Mod/Path/Gui/Resources/icons/Path-Area-Workplane.svg new file mode 100644 index 000000000..1498ef33f --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Area-Workplane.svg @@ -0,0 +1,676 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + Path-FaceProfile + 2016-01-19 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/Path/Gui/Resources/icons/Path- + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py index 61c10a7ae..be7d46a6a 100644 --- a/src/Mod/Path/InitGui.py +++ b/src/Mod/Path/InitGui.py @@ -86,7 +86,7 @@ class PathWorkbench (Workbench): threedopcmdlist = ["Path_Surfacing"] modcmdlist = ["Path_Copy", "Path_CompoundExtended", "Path_Array", "Path_SimpleCopy" ] dressupcmdlist = ["PathDressup_Dogbone", "PathDressup_DragKnife", "PathDressup_HoldingTags"] - extracmdlist = ["Path_SelectLoop", "Path_Area"] + extracmdlist = ["Path_SelectLoop", "Path_Area", "Path_Area_Workplane"] #modcmdmore = ["Path_Hop",] #remotecmdlist = ["Path_Remote"]